- Akku Tests und Balkonkraftwerk Speicher         
Seite 2 von 2 ErsteErste 12
Ergebnis 11 bis 20 von 20

Thema: Timer1 verwenden

  1. #11
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.02.2006
    Alter
    37
    Beiträge
    140
    Anzeige

    Praxistest und DIY Projekte
    Ok, anscheinend ist das nicht mein Tag, oder ich bin zu dumm um es zu verstehen. Aus irgend einem Grund Löst dieser Timer keinen Interrupt aus.

    In der Lib steht nun folgendes:
    Code:
    //alle 20ms auslösen auf COMPA
    		TCCR1A=   (0 << COM1A1)
    				| (0 << COM1A0)
    				| (0 << COM1B1)
    				| (0 << COM1B0)
    				| (0 << FOC1A)
    				| (0 << FOC1B)
    				| (0 << WGM11)
    				| (0 << WGM10);
    				
    		TCCR1B=   (0 << ICNC1)
    				| (0 << ICES1)
    				| (0 << WGM13)
    				| (0 << WGM12)
    				| (1 << CS12)	//Prescaller = 256
    				| (0 << CS11)
    				| (0 << CS10);
    		
    		OCR1A = 625;
    		TCNT1 = 0;			//Reset
    		
    		TIMSK =   (0 << TICIE1)
    				| (1 << OCIE1A)
    				| (0 << OCIE1B)
    				| (0 << TOIE1);
    Damit sollte 20ms nach dem Reset des Timers (TCNT1=0) ein Interrupt ausgelöst werden, und zwar das TIMER1_COMPA_vect wie ich aus der Tabelle von oben herausgefunden habe (ist für ATmega32, und löst bei Comparematsch A aus, wie der name schon zu vermuten lässt).

    in meinem Mainfile steht folgendes:

    Code:
    ISR(TIMER1_COMPA_vect)
    {
    uint16_t Achse1;
    uint16_t Achse2;
    
    	TCNT1 = 0;		//reset
    	
    	DDRC |= IO_PC5;
    	DDRC |= IO_PC3;
    
    	Achse1=Greifer_Achse1*10+250;
    	Achse2=Greifer_Achse2*10+250;
    
    	PORTC |= IO_PC5;
    	delay_us(Achse1);
    	PORTC &= ~IO_PC5;
    
    	PORTC |= IO_PC3;
    	delay_us(Achse2);
    	PORTC &= ~IO_PC3;
    	
    	setCursorPosLCD(1,0);
    	writeStringLCD_P("Interrupt TIMER1");	
    }
    Da ich die Servos noch nicht habe, habe ich mir gedacht ich gebe einfach mal etwas auf dem Display aus, und zwar "Interrupt TIMER1", das funktioniert allerdings nicht, da er anscheinend niemals in dieses unterprogramm wechselt.

    Ich habe anschließend Zyklisch abgefragt wie der Wert des Timers ist, das war zwar sehr verwirrend, da sich das ziemlich schnell wechselt, doch ich kann sagen, das er läuft, und zwar von 0 - 65535.

    Frage: Warum passiert nix?

  2. #12
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Damit man den CTC mode kriegt, müßte man WGM12 = 1 setzen. Der Interrupt sollte aber trotzdem gehen, wenn auch seltener. Könnte es sein dass ein globales SEI() fehlt ?

  3. #13
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.02.2006
    Alter
    37
    Beiträge
    140
    ich verwende nicht den CTC mode, sondern Compare Match A, da du mir vorher gesagt hast das der CTC mode keine Interrupt auslöst, das sei() ist vorhanden, und es wird nichteinmal nach 5 minuten ein Interrupt ausgelöst

    EDIT: Habe das gleiche mit WGM12 gesetz versucht, hat allerdings nichts geändert.

  4. #14
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.02.2006
    Alter
    37
    Beiträge
    140
    Aha, einmal drüber geschlafen, und schon habe ich die Lösung, das Programm war eigentlich richtig, nur dass ich nicht gewusst habe das das TIMSK register schon bei Timer0 verwendet wird. Bisher dachte ich immer das man mit dem Befehl (1<<OCIE1A) nur dieses eine Bit verändert, dann habe ich an den Satz von SlyD gedacht, der sagte: "C-Compiler finden fast garNICHTS selber" und das bezieht sich sogar auf Bitoperationen Naja, jetzt steht das eben weiter unten und wird nicht überschrieben und das beste ist: JETZT FUNKTIONIERTS!!! Danke für eure Hilfe, ich glaube ohne euch würde ich noch einige Tage daran sitzen und dann den RP6 im Schrank verrotten lassen, aber so kann er mir vielleicht einmal mein Glas bringen, das Ziel ist doch immer "James, hole mir den Kaffee!"

  5. #15
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    09.04.2008
    Beiträge
    384
    Bei mir functioniert das mit folgende code :
    Code:
    /*
    Timer 1 is used for servo-puls generation
    */
    ISR(TIMER1_COMPA_vect)
    	{
    	if(servocount==0){PORTC|=IO_PC2;OCR1A=servo4;}
    	if(servocount==1){PORTC&= ~IO_PC2;PORTC|=IO_PC3;OCR1A=servo3;}
    	if(servocount==2){PORTC&= ~IO_PC3;PORTC|=IO_PC5;OCR1A=servo2;}
    	if(servocount==3){PORTC&= ~IO_PC5;PORTC|=IO_PC7;OCR1A=servo1;}
    	if(servocount==4){PORTC&= ~IO_PC7;PORTD|=IO_PD5;OCR1A=servo5;}
    	if(servocount==5){PORTD&= ~IO_PD5;OCR1A=250*12;}
    	servocount++;
    	if(servocount>5) servocount=0;
    	}
    /*
    	Timer 1 is free for your application!
    	Prescaler = 64, CTC mode, 4µs resolution
    */
    	TCCR1A = (0<<WGM10)
    			|(0<<WGM11)
    			|(0<<COM1A1)
    			|(0<<COM1B1);
    			
    	TCCR1B = (1<<WGM12)
    			|(0<<CS12)
    			|(1<<CS11)
    			|(1<<CS10);
    	OCR1A = 1000;	
    // Enable timer interrupts:
    	TIMSK =   (1 << OCIE0)|(1<<OCIE1A); 	
    uint16_t servo1=350;
    uint16_t servo2=350;
    uint16_t servo3=250;
    uint16_t servo4=500;
    uint16_t servo5=350;
    
    uint8_t servocount=0;
    void servo (uint8_t nummer,uint16_t puls)
    {
    if (puls<200) puls=200;		//beveiliging tegen extreme pulslengte!!
    if (puls>550) puls=550;		//beveiliging tegen extreme pulslengte!!
    if(nummer==1) servo1=puls;
    if(nummer==2) servo2=puls;
    if(nummer==3) servo3=puls;
    if(nummer==4) servo4=puls;
    if(nummer==5) servo5=puls;
    }
    Timer 1 wird genutzt um servopulsen zu generieren. Forteil : lauft ohne uberhead, da wird nur eine Timerinterrupt getriggerd jeden 1 bis 2mS, je nach den Pulslaenge von actuele Servo.

  6. #16
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    26.01.2008
    Ort
    Allgäu
    Alter
    36
    Beiträge
    220
    hast du das funktionierende programm jetzt in der library oder ist das dein richtiges programm?

    kannst du bitte noch alles posten, wo du für die timerprogrammierung in der lib bzw. im programm geändert hast?

    hab nämlich auch vor diesen timer demnächst zu verwenden.

    danke schon mal im voraus.

    mfg

  7. #17
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.02.2006
    Alter
    37
    Beiträge
    140
    Hallo,

    also ich habe für diese Programm eigentlich nur die Funktion initRP6 Controll etwas abgeändert:

    Code:
    void initRP6Control(void)
    {
    	portInit();		// Setup port directions and initial values.
    					// This is the most important step!
    
    	cli();			// Disable global interrupts.
    
    	// UART:
    	UBRRH = UBRR_BAUD_LOW >> 8;	// Setup UART: Baud is Low Speed
    	UBRRL = (uint8_t) UBRR_BAUD_LOW;
    	UCSRA = 0x00;
        UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
        UCSRB = (1 << TXEN) | (1 << RXEN) | (1 << RXCIE);
    	
    	// Initialize ADC:
    	ADMUX = 0; //external reference 
    	ADCSRA = (0<<ADIE) | (0<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIF);
    	SFIOR = 0;
    
    	// Initialize External interrupts - all disabled:
    	MCUCR = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00);
    	GICR = (0 << INT2) | (0 << INT1) | (0 << INT0);
    	MCUCSR = (0 << ISC2);
    	
    	
    	// 10kHz Timer 0:
    	TCCR0 =   (0 << WGM00) 
    			| (1 << WGM01) 
    			| (0 << COM00) 
    			| (0 << COM01) 
    			| (0 << CS02)  
    			| (1 << CS01) 
    			| (0 << CS00);
    	OCR0  = 199;
    	
    
    	//Timer 1 is free for your application!
    	
    	//alle 20ms auslösen auf COMPA
    	TCCR1A=   (0 << COM1A1)
    			| (0 << COM1A0)
    			| (0 << COM1B1)
    			| (0 << COM1B0)
    			| (0 << FOC1A)
    			| (0 << FOC1B)
    			| (0 << WGM11)
    			| (0 << WGM10);
    			
    	TCCR1B=   (0 << ICNC1)
    			| (0 << ICES1)
    			| (0 << WGM13)
    			| (0 << WGM12)
    			| (1 << CS12)	//Prescaller = 256
    			| (0 << CS11)
    			| (0 << CS10);
    	
    	OCR1A = 625;
    	TCNT1 = 0;			//Reset
    
    	// Timer 2 - used for beeper:
    	TCCR2 =  0; 
    	OCR2  = 0xFF; 
    
    	// Enable timer interrupts:
    	TIMSK =   (1 << OCIE0)
    			| (1 << OCIE1A); 
    
    	// SPI Master (SPI Mode 0, SCK Frequency is F_CPU/2, which means it is 8MHz 
    	// on the RP6 CONTROL M32...):
    	SPCR =    (0<<SPIE) 
    			| (1<<SPE) 
    			| (1<<MSTR) 
    			| (0<<SPR0) 
    			| (0<<SPR1) 
    			| (0<<CPOL) 
    			| (0<<CPHA);  
    	SPSR = (1<<SPI2X);
    	
    	sei(); // Enable Global Interrupts
    }
    Dieser Code steht in der Library, hier ist lediglich die Einstellung für Timer 1 und das Interrupt hinzgefügt.

    Im meinem "richtigen Programm" ^^ habe ich ein Unterprogramm geschrieben:

    Code:
    ISR(TIMER1_COMPA_vect)
    {
    uint16_t Achse1;
    uint16_t Achse2;
    
    	TCNT1 = 0;		//reset
    	
    	DDRC |= IO_PC5;
    	DDRC |= IO_PC3;
    
    	Achse1=Greifer_Achse1*10+250;
    	Achse2=Greifer_Achse2*10+250;
    
    	PORTC |= IO_PC5;
    	delay_us(Achse1);
    	PORTC &= ~IO_PC5;
    
    	PORTC |= IO_PC3;
    	delay_us(Achse2);
    	PORTC &= ~IO_PC3;
    }
    es gibt zusätzlich noch 2 Globale Variablen (Greifer_Achse1, und Greifer Achse2 vom Typ uint16_t) hier steht der jeweilige Winkelwert des servos, also für Mittelposition z.b 90. Mit 4 Tastern, die bereits auf der M32 Controll vorhanden sind, wird nun diese Variable verändert:

    Also hier nochmal mein ganzes Hauptprogramm:
    Code:
    #include "RP6ControlLib.h" 
    
    //Globale Variablen:
    uint16_t Greifer_Achse1=90;
    uint16_t Greifer_Achse2=150;
    
    //*** Unterprogramme ***
    
    ISR(TIMER1_COMPA_vect)
    {
    uint16_t Achse1;
    uint16_t Achse2;
    
    	TCNT1 = 0;		//reset
    	
    	DDRC |= IO_PC5;
    	DDRC |= IO_PC3;
    
    	Achse1=Greifer_Achse1*10+250;
    	Achse2=Greifer_Achse2*10+250;
    
    	PORTC |= IO_PC5;
    	delay_us(Achse1);
    	PORTC &= ~IO_PC5;
    
    	PORTC |= IO_PC3;
    	delay_us(Achse2);
    	PORTC &= ~IO_PC3;
    }
    
    void displayAktualisieren()
    {
    	clearPosLCD(3,10,3);
    	setCursorPosLCD(3,10);
    	writeIntegerLCD(Greifer_Achse1,DEC);
    	clearPosLCD(4,10,3);
    	setCursorPosLCD(4,10);
    	writeIntegerLCD(Greifer_Achse2,DEC);
    }
    
    
    //*** Hauptprogramm ***
    int main(void)
    {
    
    uint16_t keys;
    
    	initRP6Control();
    	initLCD();
    	
    	clearLCD();
    	_showScreenLCD_P(PSTR("! Willkommen !"),PSTR("2-Achsen-Greifer:"),PSTR("1. Achse: xxx Grad"),PSTR("2. Achse: xxx Grad"));
    	displayAktualisieren();
    	
    	while(1)
    	{
    		uint8_t key = getPressedKeyNumber();
    		switch(key)
    		{
    			case 5:
    				if ((Greifer_Achse1 <= 190) && (Greifer_Achse1 > 80))
    				{	
    					Greifer_Achse1--;
    					displayAktualisieren();
    				}
    				mSleep(15);
    				break;
    			case 4:
    				if ((Greifer_Achse1 >= 80) && (Greifer_Achse1 < 190))
    				{
    					Greifer_Achse1++;
    					displayAktualisieren();
    				}
    				mSleep(15);
    				break;
    			case 3:
    				if (Greifer_Achse2 < 170)
    				{
    					Greifer_Achse2++;
    					displayAktualisieren();
    				}
    				mSleep(5);
    				break;
    			case 2:
    				if (Greifer_Achse2 > 20)
    				{
    					Greifer_Achse2--;
    					displayAktualisieren();
    				}
    				mSleep(5);
    				break;
    		}
    	}
    	return 0;
    }
    Achja, ich verwende ein 4 Zeilendisplay, also musst du eventuell noch etwas ändern.

    mfg

  8. #18
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.803
    Hallo gerko,

    vielleicht 2 kleine Anmerkungen zu deiner Lösung:

    1.
    Ich würde die RP6ControlLib nicht verändern, sondern die Timer-Parameter im eigenen Programm aufnehmen. Das ist aber natürlich eine Geschmacksfrage.

    2.
    Du nimmst den tollen Timer1, um einen 20ms Interrupt zu erzeugen, dann machst du die Impulserzeugung von 1..2ms in der ISR(TIMER1_COMPA_vect) mit Sleep-Befehlen.

    Das ist aus 2 Gründen nicht gut:
    a) Man sollte in einer ISR keine Wartebefehle verwenden, weil dadurch an irgendeiner Stelle im übrigen Programm (das könnten auch wichtige Systemroutinen sein!) eine lange Unterbrechung entsteht. Jede ISR sollte so schnell wie möglich ablaufen!

    b) Die 20ms, die bei Servos zwischen den Impulsen liegen, sind gar nicht kritisch. Viele Servos funktionieren wunderbar, selbst wenn 30ms zwischen den Impulsen liegen (oder auch nur 10ms).
    Das heißt: Man braucht da keinen exakten Timer für die Impuls-Wiederholung alle 20ms, sondern könnte das gut z.B. mit den Stopwatches des RP6 machen.
    Wofür man den Timer1 aber gut gebrauchen könnte: Für die exakte Impulsdauer selbst (1..2ms)! Dafür sollte man ihn unbedingt nehmen.

    Gruß Dirk

  9. #19
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    09.04.2008
    Beiträge
    384
    An proevofreak :
    Ich habe die RP6Controllig geandert/erweitert. Das sind drei Sachen :
    1. Initialisierung von Timer 1 register TCCR1A, TCCR1B, TIMSK. Bei mir lauft er dan mit prescaler 64. An 16 MHz bedeutet das er jeden 4µsek hochgezahlt wird.
    2. Ab die Timer 1 die Wert von OCR1A erreicht, wird eine Interrupt Sub Routine ausgelost. Die ist dan programmiert in ISR(TIMER1_COMPA_vect) . In diesen ISR werden verschiedene Ausgangen für servopulses angesteurt.
    3. Dan habe ich auch noch in die Library diesen Servo() Function programmiert. In Hauptprogram muss ich nur diese Servo(nummer, pulslaenge) einmal aufrufen. Die Pulserzeugung wird dan automatisch von diesen ISR abgehandelt, die eine Servo nach den andere. Ca jeden 20 mS wird das ganse wieder erhohlt.

  10. #20
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.02.2006
    Alter
    37
    Beiträge
    140
    ok, das waren ja eine Menge Tips für Verbesserungen und ich habe natürlich gleich versucht das umzusetzen!

    Also der Timer 1 läuft jetzt mit einem Prescaller von 8 (für eine noch bessere Genauigkeit

    In der Interruptroutine habe ich nun eine Statemaschine verwendet, das hat den vorteil dass man es im Prinzip beliebig erweitern kann, und da die 20ms sowieso nicht so genau sind, laufen diese einfach nach dem letzen Servo ab, Also der Ablauf ist:
    Servo 1 Starten und Timer1 einstellen
    Servo 1 Stoppen
    Servo 2 Starten und Timer1 einstellen
    Servo 2 Stoppen
    20ms Warten und zurück zu Servo1

    das ganze sieht dann so aus:
    Code:
    //Globale Variablen:
    uint16_t Greifer_Achse1=90;
    uint16_t Greifer_Achse2=150;
    uint8_t State;
    
    //*** Interrupt von Timer 1 ***
    
    ISR(TIMER1_COMPA_vect)
    {
    
    #define Servo1Start 1
    #define Servo1Stop 2
    #define Servo2Start 3
    #define Servo2Stop 4
    #define Wait20 5
    
    	if (State == Servo1Start)
    	{
    		PORTC |= IO_PC5;
    		TCNT1 = 0;
    		OCR1A = Greifer_Achse1*22+400;
    		State ++;
    	}
    	else if (State == Servo1Stop)
    	{
    		PORTC &= ~IO_PC5;
    		TCNT1= 0;
    		OCR1A = 100;
    		State ++;
    	}
    	else if (State == Servo2Start)
    	{
    		PORTC |= IO_PC3;
    		TCNT1 = 0;
    		OCR1A = Greifer_Achse2*22+400;
    		State ++;
    	}
    	else if (State == Servo2Stop)
    	{
    		PORTC &= ~IO_PC3;
    		TCNT1 = 0;
    		OCR1A = 100;
    		State ++;
    	}
    	else if (State == Wait20)
    	{
    		TCNT1 = 0;
    		OCR1A = 40000;
    		State = Servo1Start;
    	}
    	else
    	{
    		DDRC |= IO_PC5;		//Ausgang Servo1
    		DDRC |= IO_PC3;		//Ausgang Servo2
    		State = Servo1Start;
    	}
    }
    Ich glaube damit habe ich eine Minimale Interruptzeit, und muss auch keine Stopwatsches verwenden, d.h. alles läuft quasi im Hintergrund ab.
    Ich hoffe ihr hab das so gemeint, allerdigns bin ich zufrieden mit dem Ergebnis, die Servos laufen nun um einiges flüsssiger und der Winkel stimmt noch genauer

    Als nächstes werde ich versuchen die überaus "hässlichen" Globalen Variablen loszubekommen, hat dafür vielleicht noch jemand einen Tipp?

    Danke an alle die mir dabei geholfen haben
    mfg Gerko

    EDIT: Eine kleine Änderung habe ich noch vorgenommen, anstadt den Timer andauern zurückzusetzen (mit TCNT1=0) zähle ich einfach immer bei OCR1A den neuen Wert dazu. Das hat den Vorteil, dass man OCR1B noch für andere Funktionen verwenden könnte, außerdem spart man damit wieder 2 Taktzyklen.

Seite 2 von 2 ErsteErste 12

Berechtigungen

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

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad