-         

Ergebnis 1 bis 9 von 9

Thema: Wieder Motorsteuerung...

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    13.04.2005
    Beiträge
    56

    Wieder Motorsteuerung...

    Anzeige

    SMARTPHONES & TABLETS-bis zu 77% RABATT-Kostenlose Lieferung-Aktuell | Cool | Unentbehrlich
    Hallo!

    Dieses Thema wurde hier schon x-mal besprochen, nur habe ich noch keine Lösung für mein Problem gefunden. Vielleicht weiß ja jemand von euch Rat.
    Am Ende soll das eine Motorregelung mit dem ATMega8 werden, doch zunächst möchte ich nur eine Steuerung aufbauen, bei der dem µC vom PC aus ein 10bit Pwmwert geschickt wird, er ihn einstellt und die Umdrehungen im 100ms Takt zählt. Aus einem alten Beitrag habe ich geschlossen, dass es möglich ist Timer, externe Interrupts und die PWM gleichzeitig zu verwenden.

    Den Impulsgeber habe ich an den Int0 angeschlossen.
    Zum Impulsgeber: 256 Impulse/Umdrehung und der Motor kann bis zu 12k U/min drehen.

    Zum Programmablauf:
    Im Hauptprogramm wird nur geschaut ob neue Zeichen am UART anliegen um diese dann auszuwerten. Den Timer0 verwende ich für den 100ms-Takt zum Auslesen des Zählers, der mit jedem Int0-Aufruf erhöht wird. Den Zählerstand schreibe ich in eine externe Variable, die bei Bedarf vom Hauptprogramm ausgelesen und an einen externen PC über UART geschickt wird.

    Folgendes Problem besteht:
    Der Controller startet sich besonders oft bei niedrigen Pwm-Werten neu, jedoch zeitlich nicht vorherzusagen. Wenn ich die Interrupts Timer0 und Int0 ausschalte, funktioniert die Pwmeinstellung wunderbar ohne das sich der µC aufhängt.
    Woran kann das liegen? Habe ich bei den Interrupteinstellungen etwas falsch gemacht?

    Hier ist mein Programm:
    Code:
    #define F_CPU 16000000
    #define UART_BAUD_RATE 19200
    
    #include <stdlib.h>
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include "uart.h"
    #include <string.h>
    
    void Pwmstatus(char *);
    unsigned long zaehler=0;
    unsigned int timer=0;
    unsigned long Ergebnis;
    
    int main(void)
    {
    
    	uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
    	sei();
    
    	DDRB |= (1<<PB1) | (1<<PB2);		// PWM für Enable1 und Enable2
    	DDRD |= (1<<PD6) | (1<<PD7);		// Für Drehrichtung 
    	PORTD |= (1<<PD6);
    	PORTD &= ~(1<<PD7);
    
    	//Pwm
    	TCCR1A = (1<<WGM10) | (1<<WGM11) | (1<<COM1A1);
    	TCCR1B = (1<<CS11) | (1<<CS10);	 
    	OCR1A = 0;
    	
    	//Interrupt0
    	GIMSK |= (1<<INT0);					//INT0 aktiviert
    	MCUCR |= (1<<ISC01) | (1<<ISC00);	//Scharf gestellt auf steigende Flanke
    
    	//Timer0
    	TCCR0 |= (1<<CS01) | (1<<CS00);		//Precsaler auf 64
    	TIMSK |= (1<<TOIE0);	//Timer0 aktivieren
    	TCNT0 = 6;				//Timerwert vorstellen auf 6 für 1ms
    	
    	unsigned int c;
    	char Eingabe[10];
    	char Ausgabe[10] = "1001";
    	int i, asciiwert,Count;
    	
    	for(;;)
    	{	
    		c = uart_getc();
            if ( c & UART_NO_DATA )
            {
    		  for(i=0;i<10;i++)			//Dieser Delay von 100ms ist sehr wichtig!!
    		  {
                _delay_ms(10);
    		  }
    		  		  
    		  if(strlen(Eingabe)==5)
    		  {	
    			asciiwert = Eingabe[0];
    			switch (asciiwert)
    			{
    
    				case 'l':
    
    					PORTD |= (1<<PD6);
    					PORTD &= ~(1<<PD7);
    					
    					Pwmstatus(Eingabe);
    					Eingabe[0] = '\0';
    					break;
    
    				case 'r':
    					
    					PORTD |= (1<<PD7);
    					PORTD &= ~(1<<PD6);
    
    					Pwmstatus(Eingabe);					
    					Eingabe[0] = '\0';
    					break;	
    				
    				case 'g':
    					itoa(Ergebnis,Ausgabe,10);
    					i = strlen(Ausgabe);
    					Ausgabe[i] = '!';
    					Ausgabe[i+1] = '\0';
    					uart_puts(Ausgabe);
    					Eingabe[0] = '\0';
    					Ausgabe[0] = '\0';
    					break;								
    						
    			}
    					 
    		  
    		  }
    	   			
            }
            else
            {
                Count = 0;
    			while (c != '!')
    			{
    				Eingabe[Count++] = c;
    				c = uart_getc();
    
    			}
    			Eingabe[Count] = '\0';
    			
            }
    		
    	}
    
    	return 0;
    }
    
    void Pwmstatus(char *buffer)
    {
    	char Buffer[5];
    	int i;
    	Buffer[0] = buffer[1];
    	Buffer[1] = buffer[2];
    	Buffer[2] = buffer[3];
    	Buffer[3] = buffer[4];
    	Buffer[4] = '\0';
    	i = atoi(Buffer);
    	OCR1A = i;
    	
    }
    
    //Externe Interrupt0 Routine
    ISR(INT0_vect)
    {
    	zaehler++;				
    }
    //Timer0 Interruptroutine
    SIGNAL (SIG_OVERFLOW0)
    {
    	timer++;
    	TCNT0=6;						//Für 1ms
    	if(timer == 100)
    	{
    		GIMSK &= ~(1<<INT0);
    		Ergebnis = zaehler;
    		timer = 0;
    		zaehler = 0;
    		GIMSK |= (1<<INT0);
    	}
    	
    }
    Vielen Dank!

    Grüsse
    Charly

  2. #2
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Ich habe mir den Sourcecode mal angesehen:

    1) Die Variablen zaehler, Ergebnis und timer:
    Bei 256 Impulsen/U und max 12000 U/min hast du in 100 ms max 5120 Impulse. Dafür brauchst du keine Long-Variablen, das passt auch locker in Ints (also zaehler und Ergebnis in unsigned int ändern). timer zählt von 0 bis 100, da reicht ein unsigned char. Durch die unnötig großen Variablen werden die ISRs nur unnötig verlangsamt.

    2) Die Variable Ergebnis im speziellen:
    Diese Variable muss als volatile deklariert werden. Außerdem muss der Zugriff auf Ergebnis in main vor Interrupts geschützt werde. Damit die Interrupts nur so kurz wie möglich blockiert werden, empfehle ich die Verwendung einer zusätzlichen Variable, also z.B. so:
    Code:
      volatile unsigned int Ergebnis;
    ...
    ...
      unsigned int tmp;
      cli();
      tmp = Ergebnis;
      sei();
      itoa(tmp,Ausgabe,10);
    3) SIGNAL (SIG_OVERFLOW0):
    Nimm die beiden GIMSK-Zeilen raus und mache daraus eine "normale" ISR. Wenn du erlaubst, dass dieser Interrupt durch den anderen unterbrochen werden kann, handelst du dir nur Scherereien ein, und es bringt dir hier keinen wirklichen Nutzen.

    4)
    Achte darauf, dass du die Optimierungen des Compilers aktiviert hast, schließlich ist das Timing ja nicht ganz unkritisch.

    5)
    Dieser Delay von 100ms ist sehr wichtig!!
    Wenn im normalen Programmablauf Delays nötig sind, damit es funktioniert, ist das immer extrem verdächtig. Da liegt dann eigentlich immer etwas im Argen. Bei dir ist es folgender Code:
    Code:
             while (c != '!')
             {
                Eingabe[Count++] = c;
                c = uart_getc();
             }
    Du versuchst hier alle Zeichen der Eingabe direkt hintereinander über uart_getc einzulesen. Das funktioniert aber nur, wenn auch schon alle Zeichen im AVR eingetrudelt sind. Wenn dieser Code mitten in der Übertragung ausgeführt wird, kracht es (und zwar richtig). Und trotz des Delays kann das jederzeit passieren. Das muss unbedingt geändert werden. Ich finde die Struktur der main überhaupt etwas verquer.

    Morgen mache ich mal einen Alternativvorschlag für die main. Im Augenblick will ich eigentlich nur noch ins Bett.
    MfG
    Stefan

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    13.04.2005
    Beiträge
    56
    Hi!

    wow. Vielen Dank für die Tips! Werde sie heute nachmittag umsetzen und gucken ob es besser läuft.

    Gruß
    Charly

  4. #4
    Benutzer Stammmitglied
    Registriert seit
    13.04.2005
    Beiträge
    56
    Hallo!

    Hab das Resetproblem gelöst. Beim Durchmessen der Vs und GND Leitungen ist mir das starke Rauschen aufgefallen. Ein 100nF Kondensator am Mega8 angelötet und siehe da, ruhe ist eingekehrt... Er resettet nicht mehr und alles funzt wunderbar!\/

    @sternst: Mich würde trotzdem interessieren wie man das Readout vom UART besser hinbekommt. In Bascom gabs ja extra ein Interrupt, der ausgeführt wurde, wenn ein Byte angekommen ist. Kann ich das in C auch irgendwie verwirklichen ohne auf die Uartbibliothek von Peter Fleury zu verzichten?

    Danke!

    Gruß
    Charly

  5. #5
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    11.12.2007
    Ort
    weit weg von nahe Bonn
    Alter
    33
    Beiträge
    2.378
    in c gibts auch nen interrupt und die passende ISR dazu

    ISR(byte_received_signal){
    uart_getc();
    }

    die register hab ich jetzt nicht parat, aber wozu gibts das datenblatt ^^

  6. #6
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Das funktioniert aber nicht zusammen mit dem Fleury-UART-Code, der benutzt nämlich selber schon diesen Interrupt.
    Man kann halt nicht beides haben. Entweder man schreibt den UART-Code selber inklusive des Interrupt-Handlings, oder man muss eben das benutzen, was die Fleury-Bibliothek anbietet.

    (Code für die main folgt noch)
    MfG
    Stefan

  7. #7
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Er resettet nicht mehr und alles funzt wunderbar!
    Aber nicht immer. Der Code aus Punkt 5 führt unweigerlich zum Crash. Vielleicht erst nach Stunden oder Tagen (hängt auch davon ab, wie viel Kommandos du an den Controller sendest), aber er kommt garantiert.

    Ok, hier also mein Vorschlag für main:

    Ich habe deinen Code doch richtig dahingehend interpretiert, dass eine Eingabe immer aus einem Buchstaben, 4 Ziffern und einem Ausrufezeichen besteht?
    Die Funktion Pwmstatus habe ich übrigens rausgeschmissen.

    Code:
    int main () {
    
    	// hier die ganzen Initialisierungen einfügen
    	
    	unsigned int c;
    	char Eingabe[6];
    	char Ausgabe[10];
    	unsigned char count = 0;
    
    	for (;;) {
    
    		// auf das nächste Zeichen warten
    		do
    			c = uart_getc();
    		while (c & UART_NO_DATA);
    		
    		if (c != '!') {
    			Eingabe[count] = c;
    			count++;
    			if (count > 5) //ups
    				count = 0;
    			continue;  // weiter mit Warten auf das nächste Zeichen
    		}
    		
    		// '!' wurde empfangen, Eingabe müsste also vollständig sein
    
    		Eingabe[count] = '\0';
    		count = 0;
    		if (strlen(Eingabe) != 5) //ups
    			continue;
    		
    		switch (Eingabe[0]) {
    		
    			case 'l':
    				PORTD |= (1<<PD6);
    				PORTD &= ~(1<<PD7);
    				OCR1A = atoi(Eingabe+1);
    				break;
    				
    			case 'r':
    				PORTD &= ~(1<<PD6);
    				PORTD |= (1<<PD7);
    				OCR1A = atoi(Eingabe+1);
    				break;
    
    			case 'g':
    				cli();
    				c = Ergebnis;  // c wird hier als Zwischenspeicher "missbraucht"
    				sei();
    				itoa(c,Ausgabe,10);
    				strcat(Ausgabe,"!");
    				uart_puts(Ausgabe);
    				break;
    		}
    
    	}
    	return 0;
    }
    Die mit "ups" markierten Stellen sind Vorsichtsmaßnahmen, damit der Controller auch dann nicht aus dem Tritt kommt, wenn über die serielle Schnittstelle mal Müll eintrifft.
    MfG
    Stefan

  8. #8
    Benutzer Stammmitglied
    Registriert seit
    13.04.2005
    Beiträge
    56
    Abend!

    Hab alles umgeschrieben und es funktioniert auch wunderbar! Hab mal die Probe aufs Example gemacht und den µC mit lauter gültigen Commandos bombardiert. Bei meinem UART-Code hats nicht lange gedauert bis nix mehr ging.
    Deinen Code konnte nichts aus der Ruhe bringen.
    Vielen Dank nochmal!

    Hab jetzt noch eine allerletzte Frage. Zusätzlich kommt eine Regelung hinzu mit gegebener Abtastzeit von 100ms. Kann ich die Regelung in die Timer0 ISR einbauen oder ist davon abzuraten und sie lieber in der main unterbringen oder dafür den Timer2 verwenden?

    Grüsse
    Charly

  9. #9
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Kann ich die Regelung in die Timer0 ISR einbauen
    Davon ist abzuraten. Grundsätzlich gilt: ISRs immer so kurz wie möglich halten.

    Mein Vorschlag:

    Nimm eine zusätzliche globale Variable:
    volatile unsigned char TimerFlag;

    Füge in die ISR ein:
    TimerFlag = 1;

    Und modifiziere in main die Schleife, die auf das nächste Zeichen wartet:
    Code:
          do {
             if (TimerFlag) {
                 TimerFlag = 0;
                 regelung();
             }
             c = uart_getc();
          } while (c & UART_NO_DATA);
    Es in die "warte auf das nächste Zeichen"-Schleife einzufügen ist allerdings ein wenig Quick&Dirty. Ich persönlich würde die Struktur von main wieder ein wenig ändern, aber dazu habe ich jetzt keine Lust.
    MfG
    Stefan

Berechtigungen

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