- LiTime Speicher und Akkus         
Ergebnis 1 bis 3 von 3

Thema: servo ansteuern über ISP

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    21.03.2004
    Ort
    73061 Ebersbach
    Alter
    55
    Beiträge
    52

    servo ansteuern über ISP

    Anzeige

    Powerstation Test
    hallo RN,

    da ich im moment noch probleme habe meine programme auf das RC-Control (ATMEGA32 16MHz) zu flashen wende ich mich an euch.

    könntet ihr den code mal anschauen ob der geht, oder noch wichtiger ob ich da logische fehler drin habe?
    der servo soll in dem beispiel an PORTB.1 hängen


    Idee: (abgewandelt aus einem c-Programm um die LED flackern zu lassen).

    ich setze einen IRQ alle 10µs - damit kann ich die 1ms die ich zum ansteuern des Servos habe in 100 schritte unterteilen kann. (1ms - 2ms= 1ms + X)

    in der ISP zähle ich mit interrupt_num_20ms_Servo1 bis ich bei 20 ms bin (Pause zwischen den signalen)

    dann setze ich das signal für 1 - 2 ms refresh


    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    // Der Servo soll an B1 hängen
    // 
    // PortB.1 und GND anschliessen.
    #define PAD_Servo1  1
    #define PORT_Servo PORTB 	
    #define DDR_Servo  DDRB 	
    
    // Der MCU-Takt. Wird gebraucht, um Timer1 mit den richtigen
    // Werten zu initialisieren. Voreinstellung ist 1MHz.
    // (Werkseinstellung für AVRs mit internem Oszillator).
    // Das Define wird nur gemacht, wenn F_CPU noch nicht definiert wurde.
    // F_CPU kann man so auch per Kommandozeile definieren, z.B. für 8MHz:
    // avr-gcc ... -DF_CPU=8000000
    //   
    // ! Der Wert von F_CPU hat rein informativen Character für
    // ! die korrekte Codeerzeugung im Programm!
    // ! Um die Taktrate zu ändern müssen die Fuses des Controllers
    // ! und/oder Quarz/Resonator/RC-Glied/Oszillator
    // ! angepasst werden!
    #ifndef F_CPU
    #define F_CPU    16000000 
    #endif
    
    // So viele IRQs werden jede Sekunde ausgelöst.
    // Für optimale Genauigkeit muss
    // IRQS_PER_SECOND ein Teiler von F_CPU sein
    // und IRQS_PER_SECOND ein Vielfaches von 100.
    // Ausserdem muss gelten F_CPU / IRQS_PER_SECOND <= 65536
    #define IRQS_PER_SECOND   10000 /* 10 µs */
    
    // Anzahl IRQs pro 10 Millisekunden
    #define IRQS_PER_10MS     (IRQS_PER_SECOND / 100)
    
    // Gültigkeitsprüfung.
    // Bei ungeeigneten Werten gibt es einen Compilerfehler
    #if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
    #   error Diese Werte fuer F_CPU und IRQS_PER_SECOND
    #   error sind ausserhalb des gueltigen Bereichs!
    #endif
    
    // Compiler-Warnung falls die Genauigkeit nicht optimal ist.
    // Wenn das nervt für deine Werte, einfach löschen :-)
    #if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
    #   warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
    #endif
    
    // Prototypen
    void timer1_init();
    
    // Variablen. Werden in der ISR benötigt
    static volatile uint8_t timer_10ms;
    
    // Die Pulslänge zwischen den Servosignalen = 20 ms
    static volatile uint8_t interrupt_num_20ms_Servo1; 
    
    // Die Pulslänge zum einstellen des Servos, sie variiert von 1ms - 2 ms 
    static volatile uint8_t interrupt_num_Xms_Servo1;
    
    // hat die ISP eine Interrupt ausgelöst (20ms PulsPause auf Servo fertig)
    // wird IRQ_Servo1 auf 1 gesetzt, so dass das Main-Programm bescheid weiß.
    // IRQ_Servo1 wird in Main gelöscht
    static volatile uint8_t IRQ_Servo1; 
    
    // IRQ_Servo1_refresh wird in der ISP auf true gesetz wenn die 20ms um sind
    // und gelöscht nach dem refresh des Servo1_steuercode - Pulses
    static volatile uint8_t IRQ_Servo1_refresh;
    
    // Der Servo1_steuercode + 1ms entpricht der Pulslänge 
    // zum Ansteuern des Servo1 
    // Servo1_steuercode ist in 100 schritte zu 10µs aufgeteilt
    static volatile uint8_t Servo1_steuercode; //wird auch in der main verwendet
    
    // //////////////////////////////////////////////////////////////////////
    // Implementierungen der Funktionen
    // //////////////////////////////////////////////////////////////////////
    
    #if !defined (TCNT1H)
    #error Dieser Controller hat keinen 16-Bit Timer1!
    #endif // TCNT1H
    
    // //////////////////////////////////////////////////////////////////////
    // Timer1 so initialisieren, daß er IRQS_PER_SECOND 
    // IRQs pro Sekunde erzeugt.
    void timer1_init()
    {
        // Timer1: keine PWM
        TCCR1A = 0;
    
        // Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
        // Timer1 läuft mit vollem MCU-Takt: Prescale = 1
    #if defined (CTC1) && !defined (WGM12)
       TCCR1B = (1 << CTC1)  | (1 << CS10);
    #elif !defined (CTC1) && defined (WGM12)
       TCCR1B = (1 << WGM12) | (1 << CS10);
    #else
    #error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
    #endif
    
        // OutputCompare für gewünschte Timer1 Frequenz
        // TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ... 
        // Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
        // MCU-Takt eine IRQ erzeugt.
        OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1); 	
    																			
    
        // OutputCompareA-Interrupt für Timer1 aktivieren
    #if defined (TIMSK1)
        TIMSK1 |= (1 << OCIE1A);
    #elif defined (TIMSK)
        TIMSK  |= (1 << OCIE1A);
    #else	 
    #error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
    #endif
    }
    
    
    // //////////////////////////////////////////////////////////////////////
    // Die Interrupt Service Routine (ISR).
    // In Interrupt_num_20ms werden die IRQs gezählt.
    // Sind 2*IRQS_PER_10MS Interrups geschehen, 
    // dann sind 20 ms vergangen. und der Servo benötigt einen neuen Impuls
    SIGNAL (SIG_OUTPUT_COMPARE1A)
    {
        // interrupt_num_20ms_Servo1 erhöhen und mit Maximalwert vergleichen
        if (++interrupt_num_20ms_Servo1 == (2*IRQS_PER_10MS))
        {
            // 20 Millisekunden sind vorbei
            // interrupt_num_20ms_Servo1 zurücksetzen
            interrupt_num_20ms_Servo1 = 0;	//evtl. unnötig hier
    		// IRQ_Servo1 setzen
    		IRQ_Servo1=1;			//damit kann man in main reagieren
    		// SETZE IRQ_Servo1_refresh setzen damit der refresh gemacht werden kann
    		IRQ_Servo1_refresh=1;
    		interrupt_num_Xms_Servo1=0;	//dient nur dazu den servo zusand zu refreshen
    
    	} //endif (++interrupt_num_20ms == 2*(IRQS_PER_10MS))
    	
    	 
    	//sende an Servo1 den Steuercode
    	if (IRQ_Servo1_refresh==1)
    	{
            PORT_Servo |= (1 << PAD_Servo1);
    
    		//diesen zustand 1ms - 2 ms lang halten
    		//abhängig von Servo1_steuercode
    		//										1ms			+ Servo1_steuercode
    		if (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10	+ Servo1_steuercode))
        	{
    			// SERVO1 aus
            	PORT_Servo &= ~(1 << PAD_Servo1);
    			// LÖSCHE IRQ_Servo1_refresh
    			IRQ_Servo1_refresh=0;
    			interrupt_num_20ms_Servo1 = 0;
    		}	//endif (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10+Servo1_steuercode))
        }	//endif (IRQ_Servo1_refresh==1)
    }
    
    // //////////////////////////////////////////////////////////////////////
    // Das Hauptprogramm: Startpunkt 
    int main()
    {
    	int iCount_Down=1; //Runter oder hochzählen 1= TRUE = runterzählen
    	int IRQ_Servo1_Count =0; // Zähler für das Warten bei Servo1
    
    
    	//Servo1_steuercode in einem Interval von 1..100 (100 * 10µs = 1ms)
    	// wird die Position des Servos angegeben.
    	// 50 ist ein mittlerer Wert
    	// static uint8_t Servo1_steuercode = 50;
    	Servo1_steuercode = 50;
     
    	// SERVO-Port auf OUT
        DDR_Servo  |= (1 << PAD_Servo1);
    
        // Timer1 initialisieren
        timer1_init();
    
        // Interrupts aktivieren
        sei();
    
        // Endlosschleife
        while (1)
        {
    		if (IRQ_Servo1!=0)
    		{
    			//IRQ_Servo1 löschen
    			IRQ_Servo1=0;
    
    			//Warte solange bis sich Servo1 erneut bewegen soll
    			// z.B. 100 ms = 10 * IRQS_PER_10MS 
    			// im gewählten Beispiel sind das 
    			// also muss ein IRQ_Servo1_Count her und abgeüprüft werden.
    			IRQ_Servo1_Count++;
    			if (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
    			{
    				// den Zähler auf null setzen für den nächstnen durchlauf
    				IRQ_Servo1_Count =0;
    				//1/100 nach links und danach nach rechts und wieder nach links
    				if (Servo1_steuercode>0 && iCount_Down==1)
    				{
    					Servo1_steuercode--;
    				} 	
    				if (Servo1_steuercode<=0)
    				{
    					iCount_Down=0;
    				}
    				if (Servo1_steuercode<100 && iCount_Down==0)
    				{
    					Servo1_steuercode++;
    				} 	
    				if (Servo1_steuercode>=100)
    				{
    					iCount_Down=1;
    				}
    			}	//endif (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
    
    		} //endif (IRQ_Servo1!=0)
    
        }	//end while (1)
    
    
     return 0;
        // main braucht eigentlich keine return-Anweisung, aber ...
    }
    edit: code mit mehr kommentaren versehen
    Das, was immer von jedermann und überall als richtig akzeptiert wurde, ist mit ziemlicher Gewißheit das Falsche.
    Paul Valéry (1871-1945), frz. Dichter

  2. #2
    Erfahrener Benutzer Robotik Visionär Avatar von Hubert.G
    Registriert seit
    14.10.2006
    Ort
    Pasching OÖ
    Beiträge
    6.220
    Der Timer läuft nicht, habe aber noch nicht geschaut warum.

    Der Fehler war bei mir, der Compiler hatte die Intialisierung des Timers weggeworfen. Die LED an PB1 blinkt im Sekundentakt.

    Hubert

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    21.03.2004
    Ort
    73061 Ebersbach
    Alter
    55
    Beiträge
    52
    hmmm, dann muss ich nochmal ans reisbrett mit dem code

    denn eigentlich sollte auf B1 eine pulspause von 20 ms kommen
    dann ein signalpuls mit 1,5ms
    danachdie 20 ms pause
    wieder ein Signal, diesmal 1,49 ms
    dann pause und so fort...

    dabei wird das signal (Theorie) immer um 10µs kleiner bis auf 1 ms
    und danch bis auf 2 ms in 10µs schritten größer.
    Das, was immer von jedermann und überall als richtig akzeptiert wurde, ist mit ziemlicher Gewißheit das Falsche.
    Paul Valéry (1871-1945), frz. Dichter

Berechtigungen

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

LiTime Speicher und Akkus