Ja, so kriegt man zwar nicht ohne Weiteres die perfekten Zeitabstände hin (bei delayms gibt mans ja direkt an), aber das auch nicht besonders schlimm.
Kann es sein, dass wenn man bei einem 0..255-Timer, bei dem man sowohl die OCR-Compare-Match ISR, als auch die Overflow-ISR verwenden will und man einen PRescaler von 0 oder 8 nimmt, die Compare-Match ISR gar nicht mehr zum Zug kommt?
Ich hab nämlich versucht eine Hardware-PWM und und eine Software-PWM gleichzeitig am selben Timer zu benutzen. Wenn ich den 64er Prescaler benutze (damit fadet die LED an der Hardware-PWM optimal) und die selbst gemachte Counter-Variable der Software-PWM immer von der Overflow-ISR weiterzählen lasse, läuft selbiges natürlich 256mal langsamer ab, als alles an der Hardware-PWM; aber es läuft.
Als ich die Software-PWM dann aber beschleunigen wollte, indem ich den Prescaler runtersetzte (egal, dass die LED an der Hardware-PWM dann nur noch flackert), hat sich an der Software-PWM-LED gar nichts mehr getan.
Ist es vielleicht so, dass die OCR-Compare-Match-ISR eine höhere Priorität hat, als die Overflow-ISR und sich die Overflow-ISR dann schon jedesmal anstellt, aber dass die Compare-Match-ISR selber so lange läuft, dass die nächste schon ansteht, sobald eine fertig ist und sich somit immer die Compare-Match-ISRs vordrängeln?
So siehts bei mir aus:
Code:#define F_CPU 1000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> volatile uint8_t OCR0Adirection=0, OCR0Avalue=1; //0->OCR0A raufzählen 1-> OCR0A runterzählen ISR(TIMER0_COMPA_vect){ if(OCR0Adirection==0){ OCR0Avalue += 1; if(OCR0Avalue==254) OCR0Adirection=1; }else //if(OCR0Adirection==1){ //kann eingespart werden { OCR0Avalue -= 1; if(OCR0Avalue==1) OCR0Adirection=0; } OCR0A = OCR0Avalue; //nur einmal schreiben, statt zweimal lesen und einmal schreiben } /* Erstmal mit nur einer LED, nämlich PB4.Nachgebildet werden soll eine Fast PWM. Bei jedem Overflow des Timers wird die Funktion einmal ausgeführt und erhöht den Counter (softTCNT). Trifft dieser den OCR-Wert der LED, wird diese angeschaltet. Erreicht der Counter sein max. und wird wieder auf 0 gesetzt, wird die LED wieder ausgeschaltet. Bei der Hardware-PWM kann man wählen, ob man den OCR-Wert in der Hauptschleife mit im Abstand von z.B. 12ms um einen bestimmten Wert heraufsetzt oder das (besser) in einer ISR macht. In letzterem Fall muss darauf geachtet werden, dass die den OCR verändernde ISR nicht zu schnell hintereinander aufgerufen wird, da man sonst wesentlich weniger als 12ms zwischen den LED-Helligkeiten haben kann und sie somit viel zu schnell fadet. Deshalb sollte der Prescaler so hoch eingestellt werden, dass die LED gerade noch nicht-flackernd aussieht und der Wert um den der OCR jedes Mal verändert wird, sollte möglichst klein sein. Bei gleichzeitiger Verwendung einer Hardware- und einer Software-PWM kann dieser hohe Prescaler zum Problem werden. Denn während die Hardware-PWM ihren Counter 256 mal erhöht, tut dies die Software-PWM (bei Verwendung des selben Timers) nur einmal. */ volatile uint8_t softLED1direction=0, softLED1OCR=0, softTCNT=0; ISR(TIMER0_OVF_vect){ //software PWM softTCNT++;//counter bei jedem aufruf weiterzählen if(softTCNT==255){//einmal pro counter-runde, also am ende, bei 255 softTCNT=0;//wird der counter auf anfang gesetzt PORTB &= ~(1<<PB4);//die led geht aus if(softLED1direction==0){//und es wird der OCR-wert der LED weitergezählt softLED1OCR++;//und zwar entweder rauf if(softLED1OCR==255) softLED1direction=1; } else{//oder runter, je nach direction softLED1OCR--; if(softLED1OCR==0) softLED1direction=0; } } if(softTCNT == softLED1OCR){//sobald der counter in einer runde (0..255) den OCR der PORTB |= (1<<PB4);//LED erreicht, wird diese angeschaltet } }/* Theoretisch sollte diese soft-PWM genauso schnell faden wie die Hardware-PWM (oben) denn auch hier wird der OCR einmal pro Counter-Zyklus verändert. Jedoch läuft das Ganze 255 mal langsamer ab, sofern man nichts am Prescaler ändert oder nicht einen Timer verwendet, mit einem Modus, dessen obere Zählgrenze kleiner (in OCR gesetzt) ist und der diese Overflow-ISR somit ofter, als alle 255 Takte ausführt. */ /* //zum Testen von Hard- und Software-PWM gleichzeitig, erstmal diese Funktion rausnehmen uint8_t CurrentLED=0; void LEDleuchten(){ if(OCR0Avalue==1 || OCR0Avalue==64 || OCR0Avalue==128 || OCR0Avalue==192 || OCR0Avalue==254){//wenn timer0 am endpunkt switch(CurrentLED){//erst die betroffene LED aus case 0: PORTB &= ~(1<<PB4); break; case 1: PORTD &= ~(1<<PD5); break; case 2: PORTA &= ~(1<<PA0); } CurrentLED++;//dann zur nächsten wechseln if(CurrentLED==3)CurrentLED=0; switch(CurrentLED){//und diese anschalten case 0: PORTB |= (1<<PB4); break; case 1: PORTD |= (1<<PD5); break; case 2: PORTA |= (1<<PA0); } while(OCR0Avalue==1 || OCR0Avalue==64 || OCR0Avalue==128 || OCR0Avalue==192 || OCR0Avalue==254){}//weil sonst sofort weitergeschaltet wird } }*/ int main (void) { TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << WGM00) | (1 << WGM01); //TCCR0B = (1 << CS00);//no Prescaler TCCR0B = (1 << CS01);//Prescaler CPU/8 //TCCR0B = (1 << CS00) | (1 << CS01);//Prescaler CPU/64 //TCCR0B = (1 << CS02);//Prescaler CPU/256 //TCCR0B = (1 << CS00) | (1 << CS02);//Prescaler CPU/1024 OCR0A = 1; //Startwert, pendelt dann zwischen 0..255 DDRB = (1 << PB2); //pin PB2 auf Ausgang (das ist OC0A) //blinkende led PB4, PD5, PA0 DDRD |= (1 << PD5); DDRB |= (1 << PB4); DDRA |= (1 << PA0); //ISRs für OCR0A Compare Match und für Timer Overflow aktivieren TIMSK = (1 << OCIE0A) | (1 << TOIE0); sei(); while(1){ //LEDleuchten(); } }







Zitieren

Lesezeichen