- LiFePO4 Speicher Test         
Seite 1 von 4 123 ... LetzteLetzte
Ergebnis 1 bis 10 von 36

Thema: Drehimpulsgeber

  1. #1
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716

    Drehimpulsgeber

    Anzeige

    Praxistest und DIY Projekte
    Hallo Leute,

    ich bin am verzeweifeln. Ich schaffe es einfach nicht vernünftig einen Drehimpulsgeber auszuwerten. Ich habe mir dafür jetzt einen extra guten (und teuren) Geber mit optischer Auswertung gekauft, um das Problem mit dem Prellen einigermassen in den Griff zu bekommen. Der Geber hat 32 Rastungen pro Umdrehung und ich würde gerne einen Impuls pro Rastung nach links oder rechts erhalten.

    Ich habe dafür sogar extra einen Atmel Tiny 2313 verbaut, der nichts anderes zu tun hat wie den Encoder auszuwerten. Leider springt der Wert trotzdem oft in die falsche Richtung, wenn ich drehe. Egal bei welcher Geschwindigkeit.

    Hier mal mein Code bis jetzt:

    Code:
    #define AVRGCC
    
    #include <avr/io.h>
    #include <compiler.h>
    
    /**
    PD2	Spur A
    PD3 Spur B
    
    PB0	links
    PB1	rechts
    
    	1	2	3	4
    A	0	1	1	0
    B	0	0	1	1
    **/
    
    #define Links1							PORTB = (PORTB | (1<<PB0))
    #define Links0							PORTB = ~(~PORTB | (1<<PB0))
    
    #define Rechts1							PORTB = (PORTB | (1<<PB1))
    #define Rechts0							PORTB = ~(~PORTB | (1<<PB1))
    
    #define SpurA							((PIND & 0b00000100) == 0)
    #define SpurB							((PIND & 0b00001000) == 0)
    
    bool lastDir = TRUE;
    
    U8 read (void)
    {
    	if (SpurA)
    	{
    		if (SpurB)
    			return (3);
    		else
    			return (2);
    	}
    	else
    	{
    		if (SpurB)
    			return (4);
    		else
    			return (1);
    	}
    }
    
    void links (void)
    {
    	U8 i1;
    	
    	Links0;
    	for (i1 = 0; i1 < 0xFF; i1++)
    		asm volatile ("NOP");
    	Links1;
    	lastDir = FALSE;
    }
    
    void rechts(void)
    {
    	U8 i1;
    	
    	Rechts0;
    	for (i1 = 0; i1 < 0xFF; i1++)
    		asm volatile ("NOP");
    	Rechts1;
    	lastDir = TRUE;
    }
    
    int main (void)
    {
    	U8 AlterStatus;
    	U8 NeuerStatus;	
    	U8 i1;
    
    	DDRB = 0b00000011;
    	PORTB = 0b00000011;
    
    	DDRD = 0b00000000;
    	PORTD = 0b00001100;
    
    	for (i1 = 0; i1 < 0xFF; i1++)
    		asm volatile ("NOP");
    
    	AlterStatus = read ();
    
    	while (1)
    	{
    		NeuerStatus = read ();
    
    		if (AlterStatus != NeuerStatus)
    		{
    			switch (AlterStatus)
    			{
    				case 1:	{	switch (NeuerStatus)
    							{
    								case 2:	{ rechts (); break; }
    								case 3:	{ if (lastDir)
    											rechts ();
    										  else
    										  	links ();
    										  break;
    										}		
    								case 4: { links ();  break; }
    							}
    							break;
    						}
    				case 2:	{	switch (NeuerStatus)
    							{
    								case 1: { links ();  break; }
    								case 3: { rechts (); break; }
    								case 4:	{ if (lastDir)
    											rechts ();
    										  else
    										  	links ();
    										  break;
    										}		
    							}
    							break;
    						}
    				case 3:	{	switch (NeuerStatus)
    							{
    								case 1:	{ if (lastDir)
    											rechts ();
    										  else
    										  	links ();
    										  break;
    										}
    								case 2: { links ();  break; }
    								case 4: { rechts (); break; }
    							}
    							break;
    						}
    				case 4:	{	switch (NeuerStatus)
    							{
    								case 1: { rechts (); break; }
    								case 2:	{ if (lastDir)
    											rechts ();
    										  else
    										  	links ();
    										  break;
    										}		
    								case 3: { links ();  break; }
    							}
    							break;
    						}
    			}
    
    			AlterStatus = NeuerStatus;
    		}
    	}
    	
    	return (0);
    }
    Wer kann mir sagen wo es hängt, oder einen Code schicken, der auch funktioniert. Der Tiny arbeitet mit dne Werkseinstellungen der Fuses und ohne Quarz mit der internen Takterzeugung..

    Viele Grüße
    Andreas

  2. #2
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Hallo,

    ich muss diesen Thread mal wieder nach oben schieben. Mein Projekt neigt sich langsam dem Ende und jetzt merke ich langsam wie schlecht der Code oben funktioniert. Ich würde sagen gefühlsmässig gehen 50% der Impulse in die falsche Richtung, oder werden gar nicht erst erkannt.

    Vielleicht liegt es auch an der Beschaltung? Da der Encoder optisch funktioniert habe ich die NPN-Ausgänge einfach direkt an die Eingänge des AVR gehängt und dessen internen Pull-Ups aktiviert.

    Wer kann mir sagen, was ich zur Verbesserung tun kann?

    Viele Grüße
    Andreas

  3. #3
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    Hallo

    Ich kann es natürlich nicht testen:
    Code:
    #define AVRGCC
    
    #include <avr/io.h>
    //#include <compiler.h>
    
    /**
    PD2   Spur A
    PD3 Spur B
    
    PB0   links
    PB1   rechts
    
       1   2   3   4
    A   0   1   1   0
    B   0   0   1   1
    **/
    
    #define Links1                     PORTB = (PORTB | (1<<PB0))
    #define Links0                     PORTB = ~(~PORTB | (1<<PB0))
    
    #define Rechts1                     PORTB = (PORTB | (1<<PB1))
    #define Rechts0                     PORTB = ~(~PORTB | (1<<PB1))
    
    #define SpurA                     ((PIND & 0b00000100) == 0)
    #define SpurB                     ((PIND & 0b00001000) == 0)
    
    char spura_akt, spura_old, spurb_akt, spurb_old, count=0, dir=0;
    
    int main(void)
    {
    	Rechts0;
    	Links0;
    	spura_akt=spura_old=SpurA;
    	spurb_akt=spurb_old=SpurB;
    	
    	while(1)
    	{
    	   do // warten bis Bitwechsel erkannt wird
    		{
    			spura_akt=SpurA;
    			spurb_akt=SpurB;
    		}while((spura_akt==spura_old) && (spurb_akt==spurb_old));
    
    		if(spura_akt==spura_old) // Bitwechsel war bei Spur B
    		{
    			if(spura_akt)
    			{
    		   	if(spurb_akt) dir=1; else dir=0; // dir==1 ist rechts/aufsteigend
    			}
    			else
    			{
    		   	if(spurb_akt) dir=0; else dir=1;
    			}
    		}
    		else // Bitwechsel war bei Spur A
    		{
    			if(spurb_akt)
    			{
    		   	if(spura_akt) dir=0; else dir=1;
    			}
    			else
    			{
    		   	if(spura_akt) dir=1; else dir=0;
    			}
    		}
    
    		spura_old=spura_akt;
    		spurb_old=spurb_akt;
    
    		if(dir)
    		{
    		   Rechts1;
    		   Links0;
    		   if(count<31) count++; else count=0;
    		}
    		else
    		{
    		   Rechts0;
    		   Links1;
    		   if(count>0) count--; else count=31;
    		}
    	}
    	return(0);
    }
    Ich hoffe, ich habe es ungefähr getroffen. Der Motor dreht dauerhaft in Richtung der letzten Geberdrehung, der Zähler wird bei jedem Geberimpuls geändert (mit Nulldurchgang).

    Gruß

    mic

    [Edit]Ähm, irgendwie verstehe ich gar nicht was du eigentlich bezwecken möchtest. Wie soll denn die Drehbewegung des Geber nach Ausgang Links-Rechts verteilt werden? Mit einer Anzeige könntest du den Zähler verfolgen und kontrollieren ob alle Geberimpulse erkannt werden/ ob der Nulldurchgang immer an der selben Stelle auftritt.
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  4. #4
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Hallo mic,

    danke für deine Unterstützung. Dummerweise kann ich es gerade auch nicht testen, da ich gerade "Feierabend" gemacht habe. Ich habe privat kein Programmiergerät für Atmels und muss das immer am Wochenende oder nach der Arbeit mit dem Teil aus der Firma machen. Aber der Code schaut gut aus. Ich hoffe ich komme morgen zum testen.

    Zum Zweck des ganzen: Das soll natürlich keinen Motor steuern, sondern zwei Tasten "tippen" (via Transistor), je nachdem in welche Richtung der Encoder gedreht wird, soll die entsprechende Taste kurz getippt werden. Deine Ausgabe am Schluß ist demnach falsch, aber das konntest du nicht wissen. Mir ging es ja um die Auswertung, und die schaut gut aus. (Die Ausgabe werde ich natürlich selbst ändern).

    Wie bereits geschrieben ist sonst nichts weiters an den Atmel angeschlossen. Der Grund für den Extra-Atmel nur für diese Aufgabe ist, dass ich bis jetzt immer Probleme mit so einem Encoder hatte und das ganze deshalb mal isoliert und ohne "störende" andere Programmteile richtig sauber lösen wollte, um dann zu verstehen, wie es funktioniert.

    Wenn es funktioniert werde ich berichten. Vielen Dank erst mal!
    Andreas

  5. #5
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Hallo noch mal,

    ich habe deine Software gerade getestet und bin sehr zufrieden. Ich würde sagen nun funktioniert die Auswertung zu ca. 95%. Hier mal mein angepasster Code für meinen Anwendungsfall:

    Code:
    #define AVRGCC 
    
    #include <avr/io.h> 
    
    /** 
    PD2		Eingang		Spur A 
    PD3		Eingang		Spur B 
    
    PB0  	Ausgang 	links 
    PB1   	Ausgang 	rechts 
    **/ 
    
    #define Links1                     PORTB = (PORTB | (1<<PB0)) 
    #define Links0                     PORTB = ~(~PORTB | (1<<PB0)) 
    
    #define Rechts1                     PORTB = (PORTB | (1<<PB1)) 
    #define Rechts0                     PORTB = ~(~PORTB | (1<<PB1)) 
    
    #define SpurA                     ((PIND & 0b00000100) == 0) 
    #define SpurB                     ((PIND & 0b00001000) == 0) 
    
    char SpurA_akt, SpurA_old, SpurB_akt, SpurB_old, count = 0, dir = 0; 
    
    int main(void) 
    { 
    	DDRB = 0b00000011;
    	PORTB = 0b00000011;
    
    	DDRD = 0b00000000;
    	PORTD = 0b00001100;
    
    	Rechts0; 
       	Links0;    
    
       	SpurA_akt = SpurA_old = SpurA; 
       	SpurB_akt = SpurB_old = SpurB; 
        
      	while (1) 
       	{ 
    
          	do // warten bis Bitwechsel erkannt wird 
          	{ 
             	SpurA_akt = SpurA; 
             	SpurB_akt = SpurB;
    
    			if (count > 0)
    			{
    				count--;
    				if (count == 0)
    				{
    					Rechts0; 
        				Links0;
    				}
    			}
          	}
    	  	while ((SpurA_akt == SpurA_old) && (SpurB_akt == SpurB_old)); 
    
          	if (SpurA_akt == SpurA_old)	// Bitwechsel war bei Spur B 
          	{ 
             	if (SpurA_akt) 
             	{ 
                	if (SpurB_akt)
    					Links1;
    				else
    					Rechts1;
             	}
             	else 
             	{
                	if (SpurB_akt)
    					Rechts1;
    				else
    					Links1;
             	} 
          	} 
          	else							// Bitwechsel war bei Spur A 
          	{ 
             	if (SpurB_akt) 
             	{ 
    	            if (SpurA_akt)
    					Rechts1;
    				else
    					Links1;
             	} 
             	else 
             	{ 
                	if (SpurA_akt)
    					Links1;
    				else
    					Rechts1;
             	} 
          	} 
    
          	SpurA_old = SpurA_akt; 
          	SpurB_old = SpurB_akt; 
    
    		count = 4;
       	} 
    
       	return(0); 
    }
    Erstaunlicherweise funktioniert das ganze besser, wenn ich die Fuse aktiviere, die den internen Clock durch 8 teilt, also wenn der AVR langsamer läuft.

    Wie könnte man das ganze jetzt noch verbessern, damit aus den 95% vielleicht 100% werden? In professionellen Geräten bekommen die das ja auch irgendwie hin.

    Viele Grüße
    Andreas

  6. #6
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    Prima :)

    Vielleicht prellt/schwingt sich der Geber kurz ein. Je langsamer der AVR-Takt umso geringer die Chance ein Prellen zu treffen:
    Code:
    ...
            while ((SpurA_akt == SpurA_old) && (SpurB_akt == SpurB_old));
            
            count=10; while(count--) asm volatile ("NOP"); // Geber entprellen
    
             if (SpurA_akt == SpurA_old)   // Bitwechsel war bei Spur B
    ...
    btw: Bei mir wird das NOP nicht rausoptimiert:
    Code:
     128 009a 7093 0000 		sts count,r23
     129 009e 872F      		mov r24,r23
     130               	.L11:
     131               	/* #APP */
     132 00a0 0000      		NOP
     133               	/* #NOAPP */
     134 00a2 8150      		subi r24,1
     135 00a4 E8F7      		brcc .L11
    Wie machen sich denn die 5% Fehler bemerkbar? Woran erkennst du das?
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  7. #7
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Hallo mic,

    die 5% passieren häufiger beim schnellen drehen. Entweder reagiert der Encoder dann gar nicht, oder es wird ein Impuls in die falsche Richtung ausgelöst. Wenn mal ein falscher Impuls dabei ist kommt es dann auch häufig vor, dass insgesamt 4 falsche Impulse generiert (natürlich pro Rastung beim Encoder) werden, bis sich das ganze wieder "gefangen" hat.

    Ich habe zum testen einfach einen zweiten AVR mit angeschlossenem LCD an die Schaltung gehängt und lasse dort einen Zähler mit Balken anzeigen. Am Balken sieht man sehr schön wenn mal was in die falsche Richtung geht.

    Ich werde mal etwas mit der "Entprell-Schleife" spielen. Vielleicht bringt es ja was.

    Viele Grüße
    Andreas

  8. #8
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Nachtrag zum NOP:

    asm volatile ("NOP");

    wird in einer Schleife nicht rausoptimiert, da es ja expliziet so vom Programmierer gewünschter ASM-Code an dieser Stelle ist.

    Ein einfaches

    nop ();

    würde aber rausoptimiert werden.

  9. #9
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    Hallo

    Wenn es nur beim schnellen Drehen auftritt könnten deine erzeugten Impulse zu lang sein:

    count = 4;

    Wenn beim Warten auf einen Flankenwechsel noch ein Links/Rechts-Impuls aktiv ist wird möglicherweise der Wechsel auf der falschen Spur erkannt. Dies könnte zum Erkennen einer falschen Zählrichtung führen. Vielleicht count verkleinern.

    Oder:
    Code:
    while ((SpurA_akt == SpurA_old) && (SpurB_akt == SpurB_old)); 
    Rechts0;
    Links0;
    ...
    damit der letze Impuls sicher beendet wird. (Eventuell anschliessend nochmals kurz verzögern damit rechts0 zu rechts1 bzw. links0 zu links1 sicher erkannt werden kann)

    Noch ein Ansatz:
    Code:
         while (1)
          {
          SpurA_old = SpurA;
          SpurB_old = SpurB;
    
             do // warten bis Bitwechsel erkannt wird
    Das würde helfen wenn die Auswertung zu lange dauert und in der Zwischenzeit erneut ein Spurwechsel auftritt.

    Ich weiß immer noch nicht wie ich auf einen Motor gekommen bin :)

    Gruß

    mic
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  10. #10
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    02.08.2006
    Ort
    Würzburg, Germany
    Beiträge
    716
    Hallo mic,

    danke für dein Mitdenken. Der Count war wirklich zu lange. Das Problem mit den mehreren falschen Impulsen in Reihe ist nun behoben. (Das lag mit an der schlampigen Auswertung im AVR mit LCD dran. Dort habe ich den gleichen IRQ für den PinChange für links und rechts verwendet und in der Routine per IF geprüft welcher Eingang gekommen ist. Somit hat bei zu langem Count immer der linke gewonnen)

    Ich würde sagen nun funktioniert die Auswertung zu 97%. Dein zweiter Ansatz ist mir auch schon eingefallen, hat aber leider nichts gebracht. 97% ist vermutlich stark übertrieben. In wirklichkeit ist es glaube ich viel schlimmer. Ich schaue mal, ob ich das mit der Kamera aufzeichnen kann, dann siehst du wie "schlimm" es noch ist.

    Ich bin mit meinem Latein erst mal wieder am Ende. Wenn ich die Fuse für den Clock/8 wieder raus nehme, wird die Auswertung deutlich schlechter. Wenn ich allersings Nops zur weiteren Verzögerung einbaue auch. Es ist wie verhext. Sollte man vielleicht hardwareseitig probieren das ganze noch etwas zu entprellen?

    Viele Grüße
    Andreas

Seite 1 von 4 123 ... LetzteLetzte

Berechtigungen

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

12V Akku bauen