Hallo Gemeinde, hier mein erster Thread in Eurem schönen Forum. Ich möchte gerne mit einem ATtiny 85 etwa alle 20ms einen Impuls von definier Breite an einem frei wählbaren Port generieren und bin dabei Programmfragmente zu testen. der Port ist im Beispiel PB4 Es wird auch etwa alle 20ms ein Impuls ausgegeben. Leider ist der Impuls nicht, wie geplant 1,5ms lang, sondern nur 60µs. Ich denke, dass der Compare_Interrupt unmittlebar aufgerufen wird und dabei der Ausgang wieder auf 0 geschaltet wird. Wo liegt mein Denkfehler? Hier der Code. // Compiler- Warnungen #ifndef F_CPU #warning "F_CPU not defined" #define F_CPU 8000000UL #endif /*********************************************************************** *************/ // Libraries #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> //#include <avr/wdt.h> // für Watchdog- Funktionen //#include <stdint.h> //#include <avr/eeprom.h> /*********************************************************************** *************/ // Pin- Belegung #define In_Port PORTB // Port für Eingänge #define Pin_Eingang PINB // Eingangssignal R/C- Empfänger #define Taster1 PB1 // Pin für Eingangssignal R/C- Empfänger #define Taster2 PB2 // Eingang für Programmier-Jumper #define Out_Port PORTB // Port für Ausgänge #define Out_PortDDR DDRB // Datenrichtungsregister für Ausgänge #define LED1 PB3 // Ausgang für Schalt LED 1 #define LED2 PB4 // Ausgang f. Status LED //#define PwmOut PB0 // Ausgang f. PWM /*********************************************************************** *************/ // Variablen //static volatile uint8_t Reading; // static volatile uint16_t OvfCount = 0; // Zähler für Anzahl der Overflows static volatile uint16_t Wert = 187; // Variable f. Wert (Mittelstellung) static volatile uint16_t ActRead; // Variable f. Wert static volatile uint16_t PulsMin = 112; // Minimale Pulslänge static volatile uint16_t PulsMax = 255; // Maximale Pulslänge static volatile uint16_t Periode = 9; // Pausenperiode in Timeroverflows static volatile uint8_t Pause = 1; // Merker f. Status Pause oder Puls static volatile uint8_t ToggleBit ; // Bit zum Toggeln /*********************************************************************** *************/ // Interruptroutinen /* Fehlerbehandlung bei Timerüberlauf -> Fehler generieren */ ISR(TIMER0_OVF_vect) { Ovf_Count(); } ISR(TIMER0_COMPA_vect) { Timer0_Trigger(); } /*********************************************************************** *************/ // Hauptprogramm int main(void) { // Vorbereitung des RC- Eingangs // RC- Eingang ist schon nach Initialisierung des AVR ein Eingang In_Port |= (1<<Taster1); // interne Pull-Up-Widerstände aktivieren In_Port |= (1<<Taster2); // interne Pull-Up-Widerstände aktivieren // Initialisierung LED- Ausgänge Out_PortDDR |= (1<<LED1) |(1<<LED2); // Datenrichtung alle Portausgänge sind jetzt high Out_Port &= ~(1<<LED1); // -> LED sind nun aus Out_Port &= ~(1<<LED2); // -> LED sind nun aus // Initialisierung Interrupteingang INT0 //MCUCR |= (1<<ISC00); // Interrupt wird bei jedem Pegelwechsel an INT0 ausgelöst //GIMSK |= (1<<INT0); // Interrupt INT0 aktivieren // Initialisierung Timer0 TIMSK |= (1<<TOIE0); // Timer0 Overflow Interrupt aktivieren // Vorbereitung Status - Flags - kein Fehler liegt an, momentan kein Datenenpfang OvfCount = 0; // settings für PWM // DDRB |= (1 << PwmOut); // LED (PWM) als Ausgang // TCCR0A |= ((1 << COM0A1) | (1 << COM0A0) | (1 << WGM01) | (1 << WGM00)) ; // set OC0A (output compare pin 0A on compare match, set WGM wave form generation mode 3 = fast PWM TCCR0B |= (1<<CS01) ; // Start Timer0 mit Vorteiler 8 // globale Interruptfreigabe sei(); /*********************************************************************** ************/ while(1) { if (OvfCount > Periode) // Puls einleiten { Out_Port |= (1<<LED2); // LED 2 an cli(); TCNT0 = 0x00; // neuen Startwert für Timer laden TCCR0A = (1<<WGM01); // Clear Timer on compare match //TCCR0B = (1<<CS01); //TIMSK &= ~(1<<TOIE0); // Overflow interrupt deaktivieren TIMSK |= (1<<OCIE0A); // Interrupt bei Compare match aktivieren (Register 0A) OCR0A = Wert; // Pulslänge in Compare Register laden sei(); OvfCount = 0; } if (Pause ==1 ) { } else { } //ActRead = OvfCount + TCNT0; // Wert von Timer lesen und overflows dazuzählen. if (!(Pin_Eingang & (1<<Taster1))) { Wert = Wert + 5; _delay_ms(10); } if (!(Pin_Eingang & (1<<Taster2))) { Wert = Wert - 5; _delay_ms(10); } /* if ( Wert <= ActRead) { Out_Port &= ~(1<<LED2); // LED2 aus } else { Out_Port |= (1<<LED2); // LED 2 an } if (OvfCount >= Periode) OvfCount = 0; */ } // End of while } void Ovf_Count() { OvfCount++; if (ToggleBit == 1) { ToggleBit = 0; Out_Port |= (1<<LED1); // LED 1 an } else { Out_Port &= ~(1<<LED1); // LED1 aus ToggleBit = 1; } } void Timer0_Trigger() { Out_Port &= ~(1<<LED2); // LED2 aus TIMSK &= ~(1<<OCIE0A); // Interrupt bei Compare match deaktivieren (Register 0A) //TIMSK |= (1<<TOIE0); // Timer0 Overflow Interrupt aktivieren TCCR0A &= ~(1<<WGM01); }
lass einfach die Finger vom TIMSK in deiner Timer0_Trigger-Routine. Um Interruptflags kümmert sich der AVR selber.
Dafür, dass du nur eine bestimmte Funktionalität testen willst, hast du mächtig viel Holz in deinem Programm. Aber ich denke dein Denkfehler liegt darin, dass du übersehen hast, dass das Compare Match Interrupt Flag (welches das Auftreten eines Compare Match signalisiert) bei JEDER Übereinstimmun von TCNT0 mit OCR0A gesetzt wird. Und dieses "jeder" ist wörtlich zu nehmen. Wenn TCNT0 auf 0 steht, und du OCR0A noch keinen Wert zugewiesen hast, dann ist das bereits eine Übereinstimmung. Und ich denke das gilt sogar dann, wenn der Timer noch gar nicht läuft (zb am Programmanfang). Übereinstimmung ist Übereinstimmung. Und es gilt auch dann, wenn der Timer zwar läuft aber die Interrupts mittels cli() diabled sind oder du den Compare Match Interrupt zwischenzeitlich abstellst. Die Übereinstimmung ist da und damit wird das Interrupt Request Flag gesetzt. Und bei nächster Freigabe kommt dann eben sofort dieser Interrupt. Es ist ein Trugschluss zu glauben, dass die Hardware erst dann loslegt, wenn du deiner Meinung nach die letzte Einstellung am Timer vorgenommen hast. D.h. es wäre eine gute Idee, wenn du hier
1 | TCNT0 = 0x00; // neuen Startwert für Timer laden |
2 | TCCR0A = (1<<WGM01); // Clear Timer on compare match |
3 | //TCCR0B = (1<<CS01);
|
4 | //TIMSK &= ~(1<<TOIE0); // Overflow interrupt deaktivieren
|
5 | TIMSK |= (1<<OCIE0A); // Interrupt bei Compare match aktivieren |
6 | (Register 0A) |
7 | OCR0A = Wert; // Pulslänge in Compare Register laden |
8 | |
9 | sei(); |
nachdem du den Wert ins OCR0A bugsiert hast und bevor du mittels sei() die Interrupts wieder freigibst, erst mal alle inzwischen aufgelaufenen Compare Match Interrupt Anforderungen wieder löscht.
@Stefan K. (skorpion64) >Ich möchte gerne mit einem ATtiny 85 etwa alle 20ms einen Impuls von >definier Breite an einem frei wählbaren Port generieren und bin dabei >nicht, wie geplant 1,5ms lang, sondern nur 60µs. Sevoansteuerung. Lange Quelltexte postet man als Anhang, siehe Netiquette. Dein Quältext sieht reichlich komplex aus. Unnötig komplex. Dabei ist es eher einfach. Lass Timer1 laufen, sodass 20ms Periodendauer rauskommen. Im Overflow Interrupt setzt du den Port, im Compare Match Interrupt löschst du ihn wieder. So einfach. Dabei musss rein gar nichts an den Timereinstellungen oder Interrupts rumgefummet werden. Die Pulsbreite änderst du, indem du OCR1A(B) änderst. Fertig.
Karl Heinz Buchegger schrieb: > D.h. es wäre eine gute Idee, wenn du hier Das wäre zwar im Kontext des Timer-Problems eine gute Idee. Aber die viel bessere Idee ist es, das Rumgepfriemel an den Timer Konfigurationsregistern sein zu lassen und den Timer einfach nur in Ruhe seinen Job machen zu lassen.
Hallo Falk, erst einmal vielen Dank für Deine Blitzantwort. Ja, natürlich Servoansteuerung. Sorry für den Quältext, aber ich habe das ganze Gerüst einmal stehen lassen. Den Timer 1 wollte ich mir für andere Sachen aufheben, deshalb gehe ich den für die Periodendauer den Umweg
@ Stefan K. (skorpion64) >Den Timer 1 wollte ich mir für andere Sachen aufheben, deshalb gehe ich >den für die Periodendauer den Umweg Du erfindest das Rad neu. Aber fünfeckig. Es hat schon seinen Sinn, den Timer 1 zu verwenden. Denn der kann 16 Bit und damit die 1-2ms sehr gut auflösen. UNd das ganz allein, ohne CPU und Interrupts mit den beiden OCR1A/B Ausgängen. Deine handgestrickte Lösung schafft das nicht mal ansatzweise.
Stefan K. schrieb: > Den Timer 1 wollte ich mir für andere Sachen aufheben, deshalb gehe ich > den für die Periodendauer den Umweg Wenn du schon unbedingt den Timer 0 benutzen willst: Lass den Timer im normalen Modus durchlaufen. Alles was du tun musst ist, dir auszurechnen wann vom letzten Compare Match aus gerechnet der nächste kommen muss. Dieses OCR Wert rechnest du dir aus und schreibst ihn ins OCR0A Register. Und zwar in der ISR. Das wird dann darauf hinauslaufen, dass du einige male hintereinander denselben OCR Wert benutzt um die Pause zwischen den Pulsen zu realisieren und einmalig einen berechneten Wert benutzt, der sich aus der zu erzeugenden Pulslänge errechnet. (Anstatt einmalig geht natürlich auch mehrmals, wenn es sich mit dem Timerzyklus ansonsten nicht ausgeht, so dass man eine vernünftige AUflösung in den Servopositionen bekommt). Auf jeden Fall gibt es keinen Grund, da an den Timer-Konfigurationsregistern rumzupfriemeln. Das lässt sich alles lediglich durch die richtigen Werte im OCR0A Register zum richtigen Zeitpunkt und Mitzählen der ISR Aufrufe erreichen. Mit dem Timer 1 geht das alles einfacher, wegen seines größeren Zählumfangds. Wozu brauchst du den? Wenn es nur darum geht Pulslängen auszumessen ... der kann auch beides miteinander.
Karl Heinz Buchegger schrieb: > D.h. es wäre eine gute Idee, wenn du hier > TCNT0 = 0x00; // neuen Startwert für Timer laden > TCCR0A = (1<<WGM01); // Clear Timer on compare match > //TCCR0B = (1<<CS01); > //TIMSK &= ~(1<<TOIE0); // Overflow interrupt deaktivieren > TIMSK |= (1<<OCIE0A); // Interrupt bei Compare match aktivieren > (Register 0A) > OCR0A = Wert; // Pulslänge in Compare Register laden > > sei(); > nachdem du den Wert ins OCR0A bugsiert hast und bevor du mittels sei() > die Interrupts wieder freigibst, erst mal alle inzwischen aufgelaufenen > Compare Match Interrupt Anforderungen wieder löscht. Hallo Karl Heinz, vielen Dank für Deinen Hinweis. Das war mir so nicht klar. Ich habe versucht mit TIFR den Flag zu löschen. Hat sich nichts geändert an der Situation. ------------------------------------------------------------------- TIMSK |= (1<<OCIE0A); // Interrupt bei Compare match aktivieren (Register 0A) TIFR &= ~(1<<OCF0A); // Interrupt Flag löschen vorm reaktivieren der Interrupts sei(); OvfCount = 0; } -------------------------------------------------------------------- Den anderen Timer brauche ich wie gesagt für andere Zwecke. Gibt es nicht einen saubern Quellcode f. einen Soft timer, welcher den 8 Bit auf 16 Bit aufmotzt? Vielen Dank
> TIFR &= ~(1<<OCF0A); // Interrupt Flag löschen vorm reaktivieren
Diese Art von Flags werden gelöscht, indem man ein 1 Bit draufschreibt.
Stefan K. schrieb: > Gibt es nicht einen saubern Quellcode f. einen Soft timer, welcher den 8 > Bit auf 16 Bit aufmotzt? Hab ich schon durchgerechnet. Geht sich mit den Zahlen nicht vernünftig aus. Wenn du den Timer bei Prescaler 1 auf 256 Schritte lässt und dich an den Overflow hängst, kriegst du 1ms gerade mal in 31 Schritte aufgelöst. Gut im CTC-Modus mit 128 wären es schon 62 Schritte. Ist aber für ein Servo immer noch sehr unbefriedigend, wenn es nicht mehr Positionen anfahren kann. Aber sieh dir mein Posting über deinem an. Da gibt es einen Vorschlag.
Karl Heinz, vielen Dank noch einmal für die gute Idee mit dem Ausrechnen der Periodendauer. Super!! Ich werde das mal umschreiben. @Falk, leider hat der Attiny 85 "nur" zwei 8 bit Timer. Viele Grüße Stefan
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.