- 3D-Druck Einstieg und Tipps         
Seite 2 von 2 ErsteErste 12
Ergebnis 11 bis 14 von 14

Thema: ATtiny2313 ISR wirft mein Timing durcheinander

  1. #11
    Neuer Benutzer Öfters hier
    Registriert seit
    23.07.2011
    Beiträge
    16
    Anzeige

    Praxistest und DIY Projekte
    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();
                           
    					
                   		
    
              
                    }
    
    }

  2. #12
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Beim Timer 0 wird erst der overflow Interrupt bedient und dann der Compare match. Beim Timer 1 ist es andersherum. Man kann im Dateblatt unter Interrupt Vectors nachsehen in welcher Reihenfolge das geht. Ohne Prescaler kann das schon passieren das die ISR zu lange braucht, mit optimierung sollte es von der Laufzeit aber noch reichen.

  3. #13
    Neuer Benutzer Öfters hier
    Registriert seit
    23.07.2011
    Beiträge
    16
    Ich habs jetzt doch nochmal mit dem Timer versucht, aber zusätzlich einen Servo angeschlossen; dadurch sieht man noch genauer wann das delay wie oft übersprungen wird. Und zwar läuft das so ab, dass der Servo (Hauptschleife) ca. 25 Schritte korrekt macht, also mit 500ms dazwischen und die nächsten zehn dann mit geschätzten 50ms delay. Komischerweise hab ich das Problem auch in diesem Fall nicht mehr, wenn ich Batterien anstatt der USB-Stromversorgung nehme. (Und wenn man dem Servo eigene Batterien gibt, flackert nichtmal irgentwas).

    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
                    {
                            OCR0Avalue -= 1;
                            if(OCR0Avalue==1) OCR0Adirection=0;
                    }
                    OCR0A = OCR0Avalue;
            }
    
    
    int main (void)
    {               
                    TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << WGM00) | (1 << WGM01)
                                                            | (1 << COM0B1) | (1 << COM0B0);//OC0B für den servo              
    
                                    //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
                                    TIMSK = (1 << OCIE0A);
                                     
                                    sei();
    
                                    uint8_t i=215;
                                    //servoausschlag liegt bei 8bit-Timer, Prescaler von 64 und Fast-PWM
                                    //bei 215: -90°    245: +90°
                    while(1){
                                   
                                            for(;i<245;i++){
                                                    OCR0B=i;
                                                    _delay_ms(500);
                                            }
                                            for(;i>=215;i--){
                                                    OCR0B=i;
                                                    _delay_ms(500);
                                            }
                          
                    }
    }

  4. #14
    Neuer Benutzer Öfters hier
    Registriert seit
    23.07.2011
    Beiträge
    16
    Nochmal eine andere Frage: Um Strom zu sparen und da der Servo nicht permanent mit Kraft die Stellung halten muss, wollte ich den jetzt ohne Timer versorgen. Und zwar über eine ganz einfache Funktion, die in einer Schleife 30 mal erst für 1-2ms ein High am Port ausgibt, gefolgt von 15ms Low. (Die Länge des High-Zustandes bestimmt die Position und da sich der Servo scheinbar nur in den kurzen 15ms lang bewegt und in der Zeit keine 180° schafft, mach ich es 30 mal).
    Meine Frage bezieht sich auf den Typ der Zählvariablen in der Schleife für die 1-2ms Wartezeit.
    Um RAM zu sparen wollte ich nämlich eine 8bit statt einer 16bit Variablen benutzen, aber leider funktioniert das nicht.

    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
    		{
    			OCR0Avalue -= 1;
    			if(OCR0Avalue==1) OCR0Adirection=0;
    		}
    		OCR0A = OCR0Avalue; 
    	}
    
    
    void moveServo(uint8_t time){
    	cli();
    	PORTB |= (1<<PB4);//Servo Stromversorgung an
    
    	uint8_t i=0;
    	uint16_t j=0; //<-----Diese Stelle ist gemeint----->
    
    	for(;i<50;i++){//es wird ein paar mal der folgende drehbefehl durchgegeben
    
    		PORTD |= (1<<PD5);//servo-takt-pin an
    		for(j=0;j<time;j++){//eine ganz bestimmte zeit lang an lassen (1-2ms)
    			_delay_us(1);}
    
    		PORTD &= ~(1<<PD5);//dann wieder aus
    		_delay_ms(15);//und bis zum nächsten warten (10-20ms)
    	}
    
    	PORTB &= ~(1<<PB4);//Servo Stromversorgung aus
    	sei();
    }
    
    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
    				DDRB |= (1 << PB4);//Strom für Servo
                   	DDRD |= (1 << PD5);//OC0B (Servo)
    				DDRA |= (1 << PA0);
    
    				//ISRs für OCR0A Compare Match 
    				TIMSK = (1 << OCIE0A);
    			   	
    				sei();
    
    				uint8_t i;
    				
                    while(1){
    					
    					if(OCR0Avalue==20)
    					{	i=70;
    						moveServo(i);
    					}
    					if(OCR0Avalue==240)
    					{	i=170;
    						moveServo(i);
    					}
    					if(OCR0Avalue==128)
    					{	i=250;
    						moveServo(i);
    					}        
                    }
    }
    Die anderen, betroffenen Variablen (i in der main und time als Parameter) sind auch nur 8bit groß.
    D.h. wenn ich die Zählvariable j auch nur 8bit groß mache, müsste es doch alles glatt laufen (einen Überlauf kanns nicht geben). Aber trotzdem scheint die Schleife mit (uint8_t j schon ganz früh abzubrechen, denn der Servo dreht sich bis zum Anschlag in die Richtung der kurzen Impulsdauern. Mit uint16_t j; gehts komischerweise.

Seite 2 von 2 ErsteErste 12

Ähnliche Themen

  1. Atmega8 identify wirft wechselnde id
    Von apohero im Forum AVR Hardwarethemen
    Antworten: 8
    Letzter Beitrag: 20.03.2008, 13:16
  2. AVR controller.. bin durcheinander
    Von Robin1508 im Forum AVR Hardwarethemen
    Antworten: 19
    Letzter Beitrag: 11.11.2007, 20:35
  3. Antworten: 6
    Letzter Beitrag: 09.09.2006, 09:46
  4. Motor bringt Controller durcheinander
    Von scales im Forum Elektronik
    Antworten: 8
    Letzter Beitrag: 01.09.2006, 17:36
  5. RN-Control piept bei Programmablauf - kommt durcheinander
    Von robinhh im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 5
    Letzter Beitrag: 10.02.2006, 08:53

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad