Forum: Mikrocontroller und Digitale Elektronik Drehrichtungsumkehr.Microcontroller Programm funzt nicht richtig.


von ömer (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

bin gerade dabei einen Motor schrittweise anzusteuern. Ich zähle die 
Inkremente des Drehgebers bis 1000 und gebe einen Impuls auf den Ausgang 
PC2 oder PC3 (rechts- oder linksdrehung des Gebers).

Fährt der Motor nun gegen einen Endanschlag (Endlage_S1 oder 
!Endlage_S2),S2 ist negiert. So soll sich die Drehrichtung, also die 
Ausgänge PC2 und PC3 vertauschen, sodass ich in die andere Richtung 
weiter verfahre. Das ganze soll immer und egal welcher Endschalter 
schaltet passieren.

Momentan klappt die Drehrichtungsumkehrung genau einmal. Wenn ich ein 
zweites mal auf den gleichen Endschalter fahren will wackelt der Motor 
in zwei Zuständen hin und her.

Vielleicht kann mir dort jemand weiter helfen, denn ich sitze schon seit 
einer Woche dran um die Drehrichtung zu änder. Das kann doch nicht so 
kompliziert sein.

Danke schon einmal.

von Karl H. (kbuchegg)


Lesenswert?

Du hast die Dinge zu sehr miteinander vermanscht, deshalb verlierst du 
den Überblick.

Schreib dir als erstes eine Funktion, die den Motor einen Schritt 
vorwärts bzw. rückwärts machen lässt. Diese Funktion kümmert sich nur um 
die Endschalter bzw. einen Motorzustand und entscheidet mit diesen 
Informationen ob der Schritt über PC2 oder PC3 abzuwickeln ist.

Wenn du diese Funktion in der Hauptschleife immer wieder mit einem 
Vorwärtsschritt aufrufst, muss der Motor brav immer von einem Anschlag 
zum anderen fahren.

Und erst dann kommt der Drehgeber ins Spiel. Dessen Pulse summierst du 
nicht in 2 getrennten Variablen auf sondern in nur einer. Der Drehgeber 
verstellt diese Variable in einem Bereich von 0 bis 1000. Drehst du 
rechts, dann erhöht sich diese 'Position' um 1. Drehst du links, dann 
erniedrigt sich die Position um 1. Kommst du auf diese Art an das 
jeweilige Bereichsende (-1 oder 1001), dann lässt du den Motor einen 
Schritt vorwärts bzw. rückwärts machen. Was vorwärts oder rückwärts 
bedeutet, braucht dich hier nicht mehr zu kümmern, das wertet die 
Funktion aus dem ersten Teilproblem schon richtig aus. Und du setzt 
natürlich auch die Positionsvariable entsprechend zurück (aus 1001 wird 
0, aus -1 wird 1000).

Dann sollte das laufen.
Wenn du nicht klar kommst, melde dich noch mal. Bin am Nachmittag wieder 
hier. Aber der Schlüssel für ein einfaches, durchschaubares Programm 
liegt darin, dass man Motor und Encoder trennt. Der Motor macht Schritte 
vorwärts/rückwärts und der Encoder gibt durch links/rechts Drehung vor 
ob ein Schritt vorwärts oder zurück auszuführen ist. Was jetzt aber 
vorwärts für den Motor bedeutet (Schritt nach links oder Schritt nach 
rechts) hat nichts mit dem Encoder zu tun, sondern ist nur davon 
abhängig auf welcher Teilstrecke der Motor gerade ist.

von ömer (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

danke für die schnelle Rückmeldung.

Ich habe mir mal die Mühe gemacht, das ganze ein wenig übersichtlicher 
und einfach zu gestalten.

Die Drehgeberauswertung ist immer noch die gleiche geblieben.

void inkrementalgeber() gibt mir die Schrittel -1 bis 1001 zurück und 
zählt in void richtung() einen "counter". der counter ist am Anfang auf 
500 gestellt.

Der Motor wird dann entweder links (motor_links) oder rechts 
(motor_rechts) angesteuert, bzw. getriggert und der counter wieder 
zurück gesetzt.


Meine Probleme mit dem neuen Code:

-der Motor trigert die ganze Zeit hin und her, wenn ich den 
Inkrementalgeber drehe, egal welche richtung.
-Die Endlagen sind noch nicht im Code, da ich nicht weiß wo ich sie 
unterbringen soll.


Vielen dank!

von ömer (Gast)


Angehängte Dateien:

Lesenswert?

Habe noch ein paar Änderungen für die Übersicht vorgenommen.

von dr.prof.schlau (Gast)


Lesenswert?

ömer schrieb:
> // external Interrupt0 und Interrupt1 konfiguration
>//  MCUCR = (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(0<<ISC00);


Soll die zweite Zeile MCUCR auskommentiert sein? So wird der 
ext.Interupt nicht initialisiert.

von ömer (Gast)


Lesenswert?

Ja der wird nicht gebraucht. Ich arbeite mit einem Timer0 overflow 
interrupt.

Der Inkrementalgeber funktionierte in den unteren Drehzahlen sehr gut, 
in den oberen hat er vorwärts und rückwärts standig durcheinandere 
gebracht.

von Karl H. (kbuchegg)


Lesenswert?

Nun ja.
Mein Rat wäre immer noch, den INkrementalgeber erst mal links liegen zu 
lassen (da betreibst nämlich zur Zeit einen 2-Frontenkrieg, für dessen 
Gewinn du noch nicht soweit bist)

Dein erstes Programm soll in der main so aussehen
1
....
2
3
#define TO_RIGHT    0
4
#define TO_LEFT     1
5
uint8_t MotorDir = TO_RIGHT;    /* in welche Richtung faehrt der Motor momentan */
6
7
/*
8
** Lässt den Motor einen Schritt nach vorne
9
**
10
**   vorne und hinten sind nicht a priori links / rechts sondern
11
**   ergeben sich. Der Motor startet mit einer Bewegung nach rechts
12
**   als 'vorne'. Ist er am rechten Rand angelangt (Endschalter), dann
13
**   wird vorne umdefiniert zu Schritt nach links
14
*/
15
void MotorStepForward()
16
{
17
  uint8_t StepDir;    // welcher Schritt soll letztendlich vom Motor
18
                      // tatsächlich gemacht werden, nachdem alles
19
                      // ausgewertet und ins Kalkül gezogen wurde
20
                      // 0 .. Schritt nach rechts
21
                      // 1 .. Schritt nach links
22
                      // also genau wie MotorDir
23
24
  // auf welcher Teilstrecke sind wir?
25
  if( MotorDir == TO_RIGHT ) {
26
    // Aha, also links nach rechts
27
28
    // Ist der rechte Rand schon erreicht (Endschalter?)
29
    if( PINB & ( 1 << PINB0 ) ) {
30
      // dann geht das so nicht, die generelle Richtung muss
31
      // umgedreht werden und der Motor macht den Schritt dann
32
      // nach links
33
      MotorDir = TO_LEFT;
34
      StepDir = TO_LEFT;
35
    }
36
    else
37
      // Motor fährt gerade von links nach rechts und der rechte
38
      // Endschalter ist nicht erreicht. Das passt, der Motor
39
      // kann den Schritt genau so machen, wie geplant
40
      StepDir = TO_RIGHT;
41
  }
42
43
  else {
44
    // der Motor fährt gerade von rechts nach links
45
46
    // ist der linke Rand schon erreicht (Endschalter?)
47
    if( PINB & (1 << PINB1) ) {
48
      // dann geht das so nicht, die generelle Richtung muss
49
      // umgedreht werden und der Motor den Schritt nach rechts ausführen
50
      MotorDir = TO_RIGHT;
51
      StepDir = TO_RIGHT;
52
    }
53
    else
54
      // das passt schon. Der Motor soll einen Schritt von links
55
      // nach rechts machen
56
      StepDir = TO_LEFT;
57
  }
58
59
  // jetzt ist bekannt, welchen Schritt der Motor wirklich machen soll
60
  // wenn er einen Schritt vorwärts tun soll
61
  // na dann machen wir den mal
62
  if( StepDir == TO_RIGHT )
63
    PORTC |= ( 1 << PC2 );
64
  else
65
    PORTC |= ( 1 << PC3 );
66
67
  _delay_ms( 8 );
68
69
  PORTC &= ~( (1 << PC3) | (1 << PC2) );
70
}
71
72
73
int main( void )
74
{
75
  DDRC = 0xFF;        // PORTC als Ausgang
76
77
  for(;;)  
78
  {
79
    MotorStepForward();        // Motor mach einen Schritt nach "vorne"
80
  }
81
}

Die Endlagenschalter musst du noch überprüfen, du hast da was von 
invertiert gesagt und wie die Zuordnung ist, ist auch noch nicht klar.

Wenn ich mich da jetzt nicht allzusehr vertan habe und du die 
Pinzuordnungen noch überprüfst, dann müsste der Motor von einer Endlage 
in die andere verfahren. In der Hauptschleife machst du aber nur 
Schritte vorwärts ohne dich um die tatsächliche Drehrichtung des Motors 
kümmern zu müssen. Das erledigt alles die Funktion MotorStepForward.
Wenn die dann klappt, dann schreibst du dir noch eine Funktion 
MotorStepBackward, bei der der Motor genau in der anderen Richtung 
verfahren wird.

Und erst dann kommt der Encoder ins Spiel!

von Karl H. (kbuchegg)


Lesenswert?

Und mir fällt gerade auf, dass die Variable StepDir in Wirklichkeit 
keiner braucht. Die ist dann, wenn sie ausgewertet wird, sowieso immer 
identisch mit MOTOR_DIR.

Die Funktion lässt sich also vereinfachen zu
1
void MakeStep( uint8_t Direction )
2
{
3
  if( MotorDir == TO_RIGHT )
4
    PORTC |= ( 1 << PC2 );
5
  else
6
    PORTC |= ( 1 << PC3 );
7
8
  _delay_ms( 8 );
9
10
  PORTC &= ~( (1 << PC3) | (1 << PC2) );
11
}
12
13
void MotorStepForward()
14
{
15
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
16
  if( MotorDir == TO_RIGHT ) {
17
    if( PINB & ( 1 << PINB0 ) ) {
18
      MotorDir = TO_LEFT;
19
    }
20
  }
21
22
  else {
23
    if( PINB & (1 << PINB1) ) {
24
      MotorDir = TO_RIGHT;
25
    }
26
  }
27
28
  MakeStep( MotorDir );
29
}
30
31
void MakeStepBackward()
32
{
33
  .... du bist drann
34
  .... am Ende wird dann wieder MakeStep aufgerufen
35
}

Ich hab die Langversion trotzdem drinnen gelassen, weil man mit den 
Kommentaren meiner MEinung nach besser nachvollziehen kann, was da 
abgeht (und das kann wichtig sein, weil du die Zuordnungen der Pins noch 
richtigstellen musst)

von ömer (Gast)


Angehängte Dateien:

Lesenswert?

Hey super. Danke für die schnelle Hilfe! Bin immer wieder erstaunt, wie 
schnell hier einem Tipps gegeben werden.

Ich werde das so mal ausprobieren. Werde aber morgen erst wieder zum 
testen kommen. Aber berichte dann...

Das Programm habe ich durch den backward ergänzt und mal eingefügt.


Ich bin forward and backward mal anhand einer zustandstabelle 
durchgegangen und es sind keine Fehler aufgetreten.
Werde es dann mit dem Inkrementalgeber noch verknüpfen müssen. Leider 
bin ich aber unschlüssig, ob ich das mit dem Timer interrupt wieder 
realisieren soll.

In meinem ersten Programm wurden bei höheren Drehzahlen (< 400 1/min), 
die Schritte an dem Motor willkürlich gesendet. D.h. es wurden plötzlich 
forward und backward erkannt und der Motor zitterte hin und her.

Bei niedrigen Drehzahlen erfolgte die Umsetzung unproblematisch.

Noch etwas zu meinem Projekt:

- Die Erkennung der Drehrichtung erfolgt über einen Inkrementalgeber.
- Pro voller Umdrehung des Ink.-Gebers soll der Motor einen Schritt 
weiter verfahren.
- Die Schrittweite übernimmt eine Steuerung mit digitalen Eingängen. 
Diese sorgt dafür, dass der Motor nur den einprogrammierten Weg 
verfährt.

von Ömer (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Herr Buchegger und alle anderen ;),

anbei mein lauffähiges Programm, welches meinen Motor Schrittweise 
verfährt.


In der "main" werden MotorStepForward(); und MotorStepBackward(); 
aufgerufen und die StepMotor(); verfährt den Motor dann Schrittweise von 
einem Endschalter zum anderen.

Jetzt geht es daran meinen Inkrementalgeber einzubinden. Dieser soll mir 
jede volle Umdrehung + Drehrichtung einer Rolle erkennen und den Motor 
einen Schritt verfahren. Die Drehrichtung wird in MotorDir gespeichert 
und die Steps zähle ich hoch bis eine Umdrehung erreicht ist und gebe 
forward und backward in der main frei. (oder was haltet ihr davon?).

Jetzt zu meiner Frage des Inkrementalgebers. Ich habe einen von 
Wachendorff mit 1000 Impulsen. Dieser hat die Anschlüsse lt. diesem 
Datenblatt 
http://www.wachendorff-automation.de/fileserver/datasheet_de/anschluss_drehgeber_inkremental.pdf 
(R05 (RS422 TTL)).
Kann ich die Anschlüsse A und B direkt mit dem Eingang des MC verbinden, 
oder muss dort noch eine bestimmte Schaltung davor?

Desweiteren stelle ich mir immer noch die Frage, ob ich den Ink-Geber 
mit einem Timer Interrupt Programm auswerten sollte?, oder gibt es 
bessere Lösungen, wie die externe Interrupt Steuerung?

Meine Drehzahl der Rolle an dem der Ink-Geber hängt, liegt bei max 
1333U/min.

Ich freue mich auf eine Antwort.

von Karl H. (kbuchegg)


Lesenswert?

Sehe ich das richtig, dass deine Forward und Backward Routinen völlig 
identisch sind? Das kann irgendwie nicht stimmen.

Bei Backward hätte ich erwartet, dass MakeStep anders aufgerufen wird, 
auch die Abfrage der Endschalter muss anders sein.

von Karl H. (kbuchegg)


Lesenswert?

MakeStep kann keine Zuweisung an MotorDir machen.
Wozu auch? Das ist nicht die Aufgabe dieser Funktion. Ihre Aufgabe ist 
es einen Schritt in eine bestimmte Richtung auszuführen. Nicht mehr und 
nicht weniger. Ihre Aufgabe ist es nicht, sich aktiv in die 
Richtungssteuerung einzumischen.

(Ausserdem ist es unklug, wenn eine lokale Variable denselben Namen wie 
eine globale Variable hat. Das führt über kurz oder lang nur zu 
Verwirrung.)

von Karl H. (kbuchegg)


Lesenswert?

Wenn du durcheinander kommst, dann mach dir Skizzen!


MotorDir (das globale) sei auf TO_RIGHT

dann muss passieren bei einem Step Forward (vorwärts)
1
                TO_RIGHT    ----->
2
                           
3
  S2                       -----> Forward       S1
4
   |-----------------------M--------------------|
Wenn MotorDir also auf TO_RIGHT steht, dann bedeutet ein Schritt nach 
vorne einen Schritt nach rechts


es muss passieren bei einem Step Backward(rückwärts)
1
                TO_RIGHT    ----->
2
            
3
  S2         Backward  <----                    S1
4
   |-----------------------M--------------------|
Steht MotorDir auf TO_RIGHT, dann bedeutet ein Schritt nach rückwärts 
einen Schritt nach links. Denn die generelle Motorrichtung ist ja nach 
rechts. Wenn du der Motor bist und dich so hinstellst, dass du nach 
rechts schaust und du machst einen Schritt zurück, dann machst du aus 
meiner Sicht einen Schritt nach links.

Dementsprechend sind auch die Endschalter zu behandeln bzw. die damit 
zusammenhängende Rinchtungsumschaltung der globalen MotorDir auf 
TO_LINKS bzw. TO_RECHTS.


1
                <-------- TO_LEFT
2
3
  S2          Forward  <----                    S1
4
   |-----------------------M--------------------|
1
                <-------- TO_LEFT
2
3
  S2                       -----> Backward      S1
4
   |-----------------------M--------------------|

IMHO ist es der größte Fehler, den man als Programmierer machen kann, 
das allereinfachste und trotzdem allerbeste Hilfsmittel das man haben 
kann, einfach zu ignorieren: Papier und Bleistift und eine einfache 
Skizze, in der man sich die Zusammenhänge klar macht.

von Ömer (Gast)


Lesenswert?

Ok, ich sehe auch gerade, dass beide gleich sind. Komisch ;) , es 
funktioniert ja.

Im MakeStep muss ich den Ausgang an und ausschalten um ein digitalen 
Impuls heraus zu bekomme. Die Zuweisung des MotorDir habe ich heraus 
genommen, war wirklich für den Ar***.

Jetzt verstehe ich nicht wirklich, was du mit dem backward meinst. Warum 
soll sie MakeStep anders aufrufen? und wie?

Ich glaube ich habe dein Prinzip des Programms noch nicht verstanden. 
TO_RIGHT und TO_LEFT zeigen mir die Richtung des Inkrementalgebers an. 
Forward und Backward lassen den Motor verfahren??

von Ömer (Gast)


Lesenswert?

Ömer schrieb:
> Ok, ich sehe auch gerade, dass beide gleich sind. Komisch ;) , es
> funktioniert ja.
>
> Im MakeStep muss ich den Ausgang an und ausschalten um ein digitalen
> Impuls heraus zu bekomme. Die Zuweisung des MotorDir habe ich heraus
> genommen, war wirklich für den Ar***.
>
> Jetzt verstehe ich nicht wirklich, was du mit dem backward meinst. Warum
> soll sie MakeStep anders aufrufen? und wie?
>
> Ich glaube ich habe dein Prinzip des Programms noch nicht verstanden.
> TO_RIGHT und TO_LEFT zeigen mir die Richtung des Inkrementalgebers an.
> Forward und Backward lassen den Motor verfahren??



Zu früh geschrieben ;). Nicht ernst nehmen....

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Bei Backward hätte ich erwartet, dass MakeStep anders aufgerufen wird,
> auch die Abfrage der Endschalter muss anders sein.

Dazu noch ein Hinweis.
Ich hab ursprünglich mit mir gerungen, ob ich TO_RECHTS als +1 und 
TO_LINKS als -1 definierten sollte.
Das hätte aber wieder die Benutzung von unsigned verhindert UND es hat 
noch einen Nachteil. Es verhindert die einfache Berechnung der 
jeweiligen Gegenrechnung. Es hat schon seinen Sinn, warum die eine 
Richtung 0 und die andere als 1 definiert ist.

Überleg mal, was das Ergebnis ist, wenn eine Variable 1 (also TO_RIGHT) 
und du rechnest

     1 - Variable

Das Ergebnis ist dann 0. In deiner Nomenklatur ist das aber genau 
TO_LEFT

Und jetzt sei die Variable TO_LEFT, also 0. Denn ergibt 1 - Variable den 
Wert 1, also TO_RIGHT

Wenn ein Schritt vorwärts also

    MakeStep( MotorDir );

ist, dann ist der zugehörige Schritt rückwärts gegeben durch

    MakeStep( 1 - MotorDir );

von Karl H. (kbuchegg)


Lesenswert?

Ömer schrieb:
> Ok, ich sehe auch gerade, dass beide gleich sind. Komisch ;) , es
> funktioniert ja.

Das kann nicht funktionieren

Wenn du dich in einen Gang stellst und abwechselnd einen SChritt nach 
vorne und einen Schrit nach hinten machst ...

   while( 1 )
   {
     MakeStepForward();
     MakeStepBackward();
   }

dann kommst du nicht vom Fleck. Da du aber schreibst ...

> In der "main" werden MotorStepForward(); und MotorStepBackward();
> aufgerufen und die StepMotor(); verfährt den Motor dann
> Schrittweise von einem Endschalter zum anderen.

.... das dein Motor von einem Endschalter zum anderen verfährt, ist das 
ganz einfach das falsche Ergebnis zum Programm.


Auch testen will gelernt sein. Vorher überlegen, was bei einem 
bestimmten Programm das Ergebnis sein muss!

von Karl H. (kbuchegg)


Lesenswert?

Ömer schrieb:

> Ich glaube ich habe dein Prinzip des Programms noch nicht verstanden.
> TO_RIGHT und TO_LEFT zeigen mir die Richtung des Inkrementalgebers an.
> Forward und Backward lassen den Motor verfahren??

Der Inkrementalgeber ist noch überhaupt nicht im Spiel

Du bist der Motor!
Du stehst in einem Gang und schaust dabei in eine bestimmte 
Gangrichtung.

Ich stehe quer zum Gang und gebe die Anweisung: mach einen Schritt 
vorwärts.
Du machst den Schritt vorwärts.

In welche Richtung (links oder rechts) hast du den Schritt aus meiner 
Sicht gemacht?


Das ist die Fragestellung um die es geht.
(Schau auf die Skizzen, damit sollte klar sein, wie das alles 
zusammenhängt)


Und erst wenn diese Koordination zwischen uns beiden klappt, dann höre 
ich auf mir vorwärts oder rückwärts auszudenken und schau auf 
irgendwelche Eingabeelemente, die mir sagen, ob ich dir vorwärts oder 
rückwärts ansagen soll.

von Ömer (Gast)


Lesenswert?

Der Inkrementalgeber erkennt entweder TO_RIGHT oder TO_LEFT.
Ich komme demnach immer zur gleichen Endlage und kann nicht mit einer 
Drehrichtung zwei Endlagen abfahren.


TO_RIGHT lässt den Motor nach rechts drehen. (Forward)

                TO_RIGHT    ----->

  S2                       -----> Forward       S1
   |-----------------------M--------------------|




TO_LEFT lässt ihn nach links drehen. (Backward)


                <-------- TO_LEFT

  S2          Backward  <----                    S1
   |-----------------------M--------------------|


Wenn man es so sieht (Danke für die Zeichnungserinnerung), dann ist es 
ja klar das es mit dem Programm so läuft.

von Karl H. (kbuchegg)


Lesenswert?

Ömer schrieb:
> Der Inkrementalgeber erkennt entweder TO_RIGHT oder TO_LEFT.

Ich klink mich jetzt aus.

Wem auch mit Anleitung nicht zu helfen ist, dem ist nicht zu helfen

>                <-------- TO_LEFT
>
>   S2          Backward  <----                    S1
>    |-----------------------M--------------------|


Wenn du im Gang stehst und nach links schaust, und ich sag dir, du 
sollst einen Schritt zurück machen, in welche Richtung bewegst du dich 
dann von mir aus gesehen? Nach links oder nach rechts?

Genau das sagt nämlich das TO_LEFT bzw das TO_RIGHT über den Zeichnungen 
aus: In welche Richtung schaut der Motor? Das ist das, was in deinem 
Programm das MotorDir ist.


Noch mal: Der Inkrementalgeber interessiert im Moment keinen!
Es geht nur um den Motor, die Richtung in die er schaut und die 
Richtungen in die er Schritte machen soll.

von Ömer (Gast)


Lesenswert?

Es ist sehr schwierig über Texte etwas herüber zu bringen und für mich 
natürlich direkt zu verstehen.

Ich klinke mich dann auch mal aus und versuche nachzuvollziehen, was du 
gemeint hast.

Ich danke dir schon einmal und melde mich dann nochmal....

von THaala (Gast)


Lesenswert?

evtl. ist Dir der wichtigste Satz von Karl Heinz entgangen.

DU BIST DER MOTOR

versetz Dich in seine Lage......

Gruß
Thilo

von Karl H. (kbuchegg)


Lesenswert?

Das soll dein Testprogramm sein
1
  while( 1 ) {
2
3
    for( i = 0; i < 100; ++i )
4
      MotorStepForward();
5
6
    for( i = 0; i < 80; ++i )
7
      MotorStepBackward();
8
  }

Der Motor bewegt sich ein kleines Stück vorwärts und dann ein Stück 
zurück. Wobei er aber nicht den kompletten Weg zurückfährt. Auf die Art 
kommt er sukzessive dem rechten Rand immer näher.

Kommt die Bewegung in den rechten Rand hinein, dann wird die Bewegung 
dort 'reflektiert' und natürlich: wenn er dann den Weg wieder zurück 
fährt, dann muss auch diese Reflexion rückgängig gemacht werden. So 
lange bis er sich dann wieder vom rechten Rand gelöst hat und sich 
wieder nach links vorarbeitet, wo dann wieder das gleiche Spielchen 
abläuft.

Solange das nicht sauber funktioniert, ist deine Motorsteuerung 
fehlerhaft.

Ob die 100 bzw 80 realistische Werte sind, weiß ich nicht, weil ich 
nicht weiß wieviele Schritte es von links nach rechts sind.

von Ömer (Gast)


Lesenswert?

MotorDir ist einfach nur die Richtung in der der Motor momentan steht. 
D.h. wenn ich ihn an eine Lineareinheit packe und diese im eingebauten 
Zustand ist, ändert sich die variable global nicht mehr. Der Motor 
schaut demnach immer nur in eine Richtung?!

von Ömer (Gast)


Angehängte Dateien:

Lesenswert?

Mit diesem Programm funktioniert das ständige hin und her fahren.

Am Wendepunkt von rechts nach links, klappt alles sehr gut.
Leider bleibt der Motor auf der Fahrt nach links an Endschalter_S2 kurz 
(1ms) stehen, bevor er wieder nach rechts fährt. Kann mir momentan nicht 
erklären, woher das kommen soll.....

von Karl H. (kbuchegg)


Lesenswert?

Schau ich mir deine MotorStepForward an
1
void MotorStepForward()
2
{
3
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
4
  if(MotorDir == TO_RIGHT) 
5
  {
6
    // Abfrage ob Bit gelöscht ist. PINB0 = 0, wenn EndSchalter1 = 1. PINB0 = 1, wenn EndSchalter1 = 0. (Aktiv Low). 
7
    if( !(Endschalter_S1) )
8
      MotorDir = TO_LEFT;
9
  }
10
11
  else 
12
  {
13
    //Abfrage ob Bit gelöscht ist. PINB1 = 0, wenn EndSchalter2 = 1. PINB1 = 1, wenn EndSchalter2 = 0. (Aktiv Low)
14
    if( !(Endschalter_S1) )
15
      MotorDir = TO_RIGHT;
16
  }
17
18
  MakeStep( MotorDir );
19
}

dann fällt mir auf, dass du anscheinend das Erreichen sowohl des linken 
Randes als auch des rechten Randes offenbar durch DENSELBEN Endsschalter 
erkennst. Mit Verlaub: Das glaub ich dir nicht. Der rechte Rand wird 
durch den einen Endschalter erkannt, der linke Rand durch den anderen.

Was du daraus lernen kannst:
Es ist nicht besonders schlau, wenn man Funktions oder Variablennamen so 
wählt, dass sie sich nur in 1 Buchstaben, der dann noch dazu am Ende des 
Namens steht, unterscheiden. Denn: Das übersieht man und man liest da 
hunderte mal drüber ohne dass einem dieser Fehler auffällt.

Nenn die Dinge so, dass du beim Lesen des Quelltextes nicht groß 
nachdenken musst! Wenn du dein Programm liest, dann ist der Idealfall 
der, dass da im Grunde komplette Sätze stehen, nur ohne ein paar 
unwichtige Füllwörter. Wenn du jemandem anhand der realen Hardware vor 
dir die Funktion erklären sollst, dann sprichst du auch nicht vom 
Enschalter 1 und Enschalter 2, sondern vom rechten Endschalter und vom 
linken Endschalter.
1
void MotorStepForward()
2
{
3
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
4
  if( MotorDir == TO_RIGHT ) 
5
  {
6
    if( !(Endschalter_Rechts) )    // Endschalter sind Low-Activ
7
      MotorDir = TO_LEFT;
8
  }
9
10
  else 
11
  {
12
    if( !(Endschalter_Links) )    // Endschalter sind Low-Activ
13
      MotorDir = TO_RIGHT;
14
  }
15
16
  MakeStep( MotorDir );
17
}

Wenn ich mir den Programmtext durchlese, dann brauch ich nicht lange 
überlegen, was jetzt S1 oder S2 ist. Da stehst mehr oder weniger 
explizit:
1
  if( MotorDir == TO_RIGHT ) 
2
  {
3
    if( !(Endschalter_Rechts) )    // Endschalter sind Low-Activ
4
      MotorDir = TO_LEFT;
5
  }
1
Wenn der Motor nach rechts schaut, wenn dabei der rechte Endschalter
2
angesprochen hat, dann drehe die Richtung um und lass ihn nach links schauen.

Lies den Programmtext und lies die umgangssprachliche Beschreibung. Das 
eine ist eine formale, C-aufbereitete Version des anderen. Aber im 
Grunde sind die beiden Dinge absolut identisch. (Das ! sollte besser da 
auch noch verschwinden. Spricht doch nix dagegen, dass du es ins Makro 
mit aufnimmst) Aber: Du denkst viel besser in der zweiten Version. Das 
die korrekt ist, kann dir jedes Grundschulkind bestätigen, denn das was 
da beschrieben ist, das ist im Grunde ziemlich banal.

denn dann geht es weiter
1
wenn allerdings der Motor nicht nach rechts schaut (dann muss er nach
2
links schauen), und wenn dann auch noch der LINKE Endschalter angesprochen
3
hat, dann muss er genauso umgedreht werden und er schaut dann eben nach
4
rechts.

nichts anderes sagt aber auch
1
  else 
2
  {
3
    if( !(Endschalter_Links) )    // Endschalter sind Low-Activ
4
      MotorDir = TO_RIGHT;
5
  }


In der Backward Funktion hast du noch mal den gleichen Fehler. Aber 
aufpassen: Wenn der Motor nach rechts schaut und er einen Schritt zurück 
machen soll, dann kann er den linken Endschalter auslösen!


Und so Kommentare mit dem PIN und ausmaskieren, die haben an dieser 
Stelle nix verloren.

von Ömer (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe das Programm jetzt noch ein wenig überarbeitet. Ich habe die 
Endschalter vertauscht. Da es aber irgendwie lief, habe ich mir darüber 
auch keine Gedanken mehr gemacht.

Da ich noch den Anfängerstatus im programmieren habe, bin ich für jeden 
Tipp dankbar.

von Karl H. (kbuchegg)


Lesenswert?

Stell dir einfach mal vor, du hättest dein Programm noch nie gesehen und 
wüsstest nichts darüber
1
    // Abfrage ob Bit gelöscht ist. PINB0 = 0, wenn EndSchalter1 = 1. PINB0 = 1, wenn EndSchalter1 = 0. (Aktiv Low). 
2
    if(Endschalter_Rechts)

inwiefern hängt dieser Kommentar mit dem if zusammen?

Im if steht: wenn der Endschalter
Implizit kann man da den Satz zu Ende führen: wenn der Endschalter 
ausgelöst hat.

Und da ist auch ok so.

Und jetzt der Kommentar dazu. Da ist von Bits die Rede und von einem 
PINB, von einem Endschalter1 und irgendwas mit Aktiv Low.

Geh davon aus, du hättest das alles vorher noch nie gesehen. Dann wirds 
dir wohl genauso gehen, wie allen anderen auch. Du fragst dich: "Hä? Was 
will mir der Autor des Kommentares hier sagen? Wie hängt das jetzt alles 
mit der banalen Aussage 'der Endschalter hat ausgelöst' zusammen? 
Interessiert mich das hier überhaupt? Ich will doch gar nicht wissen, 
wie das mit den Bits u.dgl. ist, dazu befrage ich an dieser Stelle ja 
auch einfach nur Endschalter_Rechts und das Bitgepfriemel ist in dieser 
Endschalter Abfrage irgendwie enthalten. Wenn ich diese Details wissen 
will, dann schau ich bei Endschalter_Rechts nach."

Manchmal ist es besser keinen Kommentar zu setzen. Wenn der Programmtext 
die generelle Idee an dieser Stelle ausreichend gut beschreibt, dann 
können Kommentare mehr schaden als nützen. Vor allen Dingen, wenn sich 
im Laufe der Zeit die Dinge etwas ändern und die nicht nachgezogenen 
Kommentare dann falsch werden.

Generell sollten Kommentare das WARUM beschreiben und nicht das WIE.
Wie die Abfrage erfolgt ist im Code nachvollziehbar. Aber im Code ist 
erst mal nicht direkt nachvollziehbar, warum man diese Aktion macht. 
Wenn es gelingt, das 'warum' durch geschickte Wahl von Variablen und 
Funktionsnamen unmittelbar ersichtlich zu machen, dann braucht es auch 
keinen Kommentar. Dann ist der Code sein eigener Kommentar. Eine 
Codezeile, ala
1
   if( Endschalter_rechts )     // wenn der Endschalter Rechts ausgelöst hat
ist so gesehen ein unnützer Kommentar. Denn der Code selber dokumentiert 
ausreichend gut, was hier passiert. Aus dem Kommentar kriege ich hier 
keine weitere Information, so dass der Kommentar weggelassen werden 
kann.

von Ömer (Gast)


Lesenswert?

jo da hast du Recht.
Der Kommentar steht noch vom letzten Mal dort. Ich habe mir nur den 
vermerk gemacht, dass ich den Eingang Activ low habe. Hatte nämlich 
zuvor das Problem das die Endschalter durch eine falsche Verdrahtung von 
aussen nicht erkannt wurden.

von Karl H. (kbuchegg)


Lesenswert?

Ist schon ok.
Ich erzähl dir das ja alles nicht deshalb so ausführlich, weil mir fad 
ist, sondern weil ich meine Aufgabe hier auch ein wenig darin sehe, die 
Leute wenigstens ein bischen auszubilden.

Gut.
Die 3 Funktionen
  void MotorStepForward()
  void MotorStepBackward()
  void MakeStep( uint8_t MotorDir )

ist dir da jede Codezeile klar? Weißt du von jeder Zeile exakt was sie 
macht und warum sie vorhanden ist?

Denn jetzt käme der nächste Schritt: Jetzt lehnen wir uns zurück und 
betrachten einmal kritisch das Werk, ob man es nicht noch vereinfachen 
könnte.
(Und du wirst überrascht sein, wo wir im Endeffekt landen :-) Da fällt 
dann nämlich noch einmal gut 30 bis 50% Code weg)

von Ömer (Gast)


Lesenswert?

void MotorStepForward()

wird aufgerufen, wenn ich einen Schritt vorwärts fahren will. Die 
Motorstellung wird geprüft und die Endschalter. und dann der Schritt 
to_right oder to_left über Makestep ausgeführt. MotorDir wird übergeben.

  void MotorStepBackward()

wird aufgerufen, wenn ich einen Schritt Rückwärts fahren will. Die 
Motorstellung wird geprüft und die Endschalter. und dann der Schritt 
to_right oder to_left über Makestep ausgeführt. MotorDir wird übergeben.

  void MakeStep( uint8_t MotorDir )

Ausgabe der rechts oder links bewegung an den Motor

___________________________________________________

von Ömer (Gast)


Lesenswert?

Ok, ich versuche es nochmal ein wenig genauer...
_______________________________________

void MotorStepForward()
{
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
  if(MotorDir == TO_RIGHT)
  {
    // Abfrage ob Endschalter_Rechts gesetzt
    if(Endschalter_Rechts)
    {
      MotorDir = TO_LEFT;
    }
  }

  else
  {
    // Abfrage ob Endschalter_Links gesetzt
    if(Endschalter_Links)
    {
      MotorDir = TO_RIGHT;
    }
  }

  MakeStep(MotorDir);
}
___________________________

Wenn ich den Motor vorwärts drehen will, wird geprüft in welcher 
Richtung der Motor momentan steht. MotorDir==TO_RIGHT oder TO_LEFT (0 
oder 1)

if (rechts) oder else (links).
Es werden nun die möglichen Endschalter auf den Zustand überprüft.

if (rechts) und if(Endschalter_Rechts) dann drehe nach links 
(MotorDir=1), ansonsten drehe rechts (MotorDir=0).

if (links) und if(Endschalter_Links) dann drehe nach rechts 
(MotorDir=0), ansonsten drehe links (MotorDir=1).

rechts oder links wird mit MotorDir an MakeStep übergeben.

_______________________________

void MotorStepBackward()
{
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
  if(MotorDir == TO_RIGHT)
  {
    // Abfrage ob Endschalter_Links gesetzt
    if(Endschalter_Links)
    {
      MotorDir = TO_LEFT;
    }
  }

else
  {
    // Abfrage ob Endschalter_Rechts gesetzt
    if(Endschalter_Rechts)
    {
      MotorDir = TO_RIGHT;
    }
  }

  MakeStep( 1 - MotorDir );
}

___________________________________________

Wenn ich den Motor rückwärts drehen will, wird geprüft in welcher 
Richtung der Motor momentan steht. MotorDir==TO_RIGHT oder TO_LEFT 
(wieder 0 oder 1)

if (rechts) oder else (links).
Es werden nun die möglichen Endschalter auf den Zustand überprüft.

if (rechts) und if(Endschalter_Links) dann drehe nach links 
(MotorDir=1), ansonsten drehe rechts weiter (MotorDir=0).

if (links) und if(Endschalter_Rechts) dann drehe nach rechts 
(MotorDir=0), ansonsten drehe links (MotorDir=1).

Wenn der Motor rechts steht und kein Endschalter ist geschaltet, dann 
steht in MakeStep (1-0) und der Motor dreht links (zurück).
Wenn der Motor links steht und kein Endschalter ist geschaltet, dann 
steht in MakeStep (1-1) und der Motor dreht rechts (vorne).

Ist einer der Endschalter betätigt ändert sich die Richtung.

_____________________________________________

void MakeStep( uint8_t MotorDir )
{
  if( MotorDir == TO_RIGHT )
  {

    PORTC |= ( 1 << PC1 );

    _delay_ms(60);

    PORTC &=~ (1 << PC1);

    _delay_ms(60);
  }
else
  {
    PORTC |= ( 1 << PC2 );

    _delay_ms(60);

    PORTC &=~ (1<<PC2);

    _delay_ms(60);
  }
}

_____________________________________________

MakeStep bekomme MotorDir übergeben und prüft ob eine 1 oder 0 drin 
steht und lässt den if Motor rechts = 0 rechts verfahren.
Ansonsten else Motor links = 1 links verfahren.

_________________________________________________

Damit komme ich wieder in die main loop wo ich zuvor herausgesprungen 
bin.

von Ömer (Gast)


Lesenswert?

Grübel

30-50% ??? ;)

von ömer (Gast)


Lesenswert?

Meintest du mit vereinfachen:

- Allgemeine C-Programmierung so verändern, dass der Code kürzer wird?
- Den Ablauf des Programms vereinfachen, sodass funktionen auch komplett 
ausgelassen werden können.

Ich habe nämlich auch nach mehrfachen drüber schauen, keineVeränderung 
vornehmen können :(. Mir ist aber auch jetzt erst eine Vereinfachung der 
C-Programmierung eingefallen, falls du diese damit meintest.

Ansonsten würde ich mich am Freitag mal mit dem Inkrementalgeber 
beschäftigen und den Timer2 im CTC Modus programmieren um ihn zu testen.

von Karl H. (kbuchegg)


Lesenswert?

Sorry. Konnte nicht früher

Der springende Punkt liegt hier ...
1
void MotorStepForward()
2
{
3
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen
4
  if(MotorDir == TO_RIGHT) 
5
  {
6
    // Abfrage ob Bit gelöscht ist. PINB0 = 0, wenn EndSchalter1 = 1. PINB0 = 1, wenn EndSchalter1 = 0. (Aktiv Low). 
7
    if(Endschalter_Rechts)       
8
    {
9
      MotorDir = TO_LEFT;
10
    }
11
  }
12
13
  else 
14
  {
15
    //Abfrage ob Bit gelöscht ist. PINB1 = 0, wenn EndSchalter2 = 1. PINB1 = 1, wenn EndSchalter2 = 0. (Aktiv Low)
16
    if(Endschalter_Links)    
17
    {
18
      MotorDir = TO_RIGHT;
19
    }
20
  }
21
22
  MakeStep(MotorDir);
23
}

... im ersten Teil.

Was passiert hier?
Es wird geprüft, ob der jeweilige Endschalter erreicht ist und wenn ja, 
wird die 'Blickrichtung' umgekehrt (Ich vermeide bewusst das Wort 
Drehrichtung)

Das SChema ist

   if( Rechts )
     ....
     neue Richtung ist Links

   if( Links )
     ....
     neue Richtung ist Rechts

d.h. die neue Richtung ist immer das Gegenteil der alten Richtung. Nun 
gibt es aber eine einfache Möglichkeit um mit der Kentniss der alten 
Richtung die neue Richtung zu bestimmen. Ist ja immer das Gegenteil
1
void MotorStepForward()
2
{
3
  if(MotorDir == TO_RIGHT) 
4
  {
5
    if(Endschalter_Rechts)       
6
      MotorDir = 1 - MotorDir;
7
  }
8
  else 
9
  {
10
    if(Endschalter_Links)    
11
      MotorDir = 1 - MotorDir;
12
  }
13
14
  MakeStep(MotorDir);
15
}

und jetzt eröffnet sich plötzlich eine Möglichkeit, wie man die beiden 
Fälle zusammenziehen kann, denn die Auswirkung (MotorDir = 1-MotorDir) 
ist in allen Fällen immer die gleiche.
1
void MotorStepForward()
2
{
3
  if( (Endschalter_Rechts && MotorDir == TO_RIGHT) ||
4
      (Endschalter_Links && MotorDir == TO_LEFT) )       
5
    MotorDir = 1 - MotorDir;
6
7
  MakeStep(MotorDir);
8
}

und für die andere Richtung lässt sich dasselbe umgekehrt machen
1
void MotorStepBackward()
2
{
3
  if( (Endschalter_Rechts && MotorDir == TO_LEFT) ||
4
      (Endschalter_Links && MotorDir == TO_RIGHT) )       
5
    MotorDir = 1 - MotorDir;
6
7
  MakeStep(1 - MotorDir);
8
}

als nächsten Schritt wollte ich dann die beiden Endschalter Abfragen aus 
den Einzelfunktionen in eine eigene Funktion zusammenziehen. Ich hab das 
aber nicht groß vorher durchdacht, das bringt keine großartige 
Vereinfachung mehr sondern ganz im Gegenteil:
1
void ChekForTurn( uint8_t DirRight, uint8_t DirLeft )
2
{
3
  if( (Endschalter_Rechts && MotorDir == DirRight) ||
4
      (Endschalter_Links && MotorDir == DirLeft) )       
5
    MotorDir = 1 - MotorDir;
6
}
7
8
void MotorStepForward()
9
{
10
  CheckForTuen( TO_RIGHT, TO_LEFT );
11
  MakeStep(MotorDir);
12
}
13
14
void MotorStepBackward()
15
{
16
  CheckForTuen( TO_LEFT, TO_RIGHT );
17
  MakeStep(1 - MotorDir);
18
}

Ich finde der letzte Schritt verschleiert mehr als er an Vereinfachung 
bringt. So gesehen war ich da etwas voreilig. Ich hatte nicht bedacht, 
dass beim Abfragen der Endschalter die Kentniss der Motorblickrichtung 
notwendig ist.

von ömer (Gast)


Lesenswert?

Danke für den Hinweis.
Ich denke man kann dort auch die Laufzeit des Programms verkürzen, oder 
sehe ich das falsch. Wenn ich nämlich im AVR Studio simuliere, springt 
er von Zeile zu Zeile und erhöht meine Durchlaufzeit.

Ich habe in diesem Zuge auch mal versucht den MakeStep ein wenig kürzer 
zu fassen. Aber dort wird es bei mir nur länger anstatt kürzer.

von ömer (Gast)


Lesenswert?

Kann es sein, dass du dir bei dir das MakeStep falsch ist. Du drehst ja 
vorher schon MotorDir um, dann braucht man es doch nicht nochmals 
umdrehen??
Also Ich habe es so gemacht.

void MotorStepForward()
{
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen

  // Umkehrung von MotorDir bei Erreichen der Endlagen
  if ((MotorDir == TO_RIGHT && Endschalter_Rechts)|| (MotorDir == 
TO_LEFT && Endschalter_Links))

    MotorDir =  1 - MotorDir;

  // Vorwärts
  MakeStep (MotorDir);
}


void MotorStepBackward()
{
  // bei erreichen der Endschalter die Richtung der Schritte umdrehen

  // Umkehrung von MotorDir bei erreichen der Endlagen
  if((MotorDir == TO_RIGHT && Endschalter_Links) || (MotorDir == TO_LEFT 
&& Endschalter_Rechts))
  {
    MotorDir =  1 - MotorDir;
  }

  // Rückwärts
  MakeStep (MotorDir);
}

von ömer (Gast)


Lesenswert?

Denkfehler: Du hattest Recht!

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Danke für den Hinweis.
> Ich denke man kann dort auch die Laufzeit des Programms verkürzen, oder
> sehe ich das falsch.

Laufzeit ist völlig wurscht, solange du deine Pulse mittels _delay_ms 
erzeugst. Dort vergeht die Zeit. Ob die Pulslogik 10 oder 15 
Nanosekunden länger dauert oder nicht, spielt dem gegenüber keine Rolle.

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, damit ist jetzt ein Zustand der Motorsteuerung erreicht, der 
für die nächste Zeit ausreichend ist und gut funktioniert. Den Teil 
würde ich jetzt erst mal als abgeschlossen sehen und mich jetzt dem 
Drehencoder zuwenden. Aus dessen Auswertung ergibt sich dann ob Forward 
oder Backward aufgerufen werden soll.

Darf ich mal fragen, was ds ganze eigentlich wird?
Ich finde nämlich die Aufgabenstellung seltsam.
Dreh ich den Encoder nach rechts, fährt der Motor mit Blickrichtung 
vorwärts, wobei er in den Endlagen umdreht.
Dreh ich den Enocde nach links, fährt er rückwärts, ebenfalls mit 
Richtungsumkehr.
Ich zerbrech mir jetzt schon einige Zeit den Kopf, bei welcher 
Aufgabenstellung so etwas auftritt, aber mir fällt dazu nichts ein.

von ömer (Gast)


Lesenswert?

Das gibt eine Wickelvorrichtung für eine Seiltrommel ;).
Das Seil soll vor der Trommel hin und her gefahren werden, damit nicht 
immer so eine dicke Wulst entsteht. Die Drehrichtung der Trommel 
entspricht dann dem Verfahrweg des Motors. Die Drehrichtung kann sich 
aber ändern und es wird ca. 2-lagig auf und abgewickelt.

Ich habe mir eben mal einen Timer aufgebaut, der mir das A/B Siganl 
abtastet.
Ich benutze den Timer2 im CTC Modus.

Ich habe die BErechnung für den Compare Modus so gestaltet:

- Trommel dreht maximal mit 1333U/min=22,2U/s
- 22,2U/s * 1000 Impulse/U = 22,2*10^3Impulse/s
- Alle 45us bekomme ich ein Impuls
- Doppelte Abtastung ergibt ca. 20us ein overflow oder compare match

_________________________
// 8-Bit Timer2 Konfiguration

  TCCR2 = (1<<WGM21); // CTC Mode
  TIMSK |= (1<<OCIE2);  // Compare Interrupt erlauben
  TCCR2 = (1<<CS20);  // ohne Vorteiler; 3,6864MHz

  // Vergleichswert; TCNT2 vergleicht seinen Zähler mit OCR2 und
  // 3686400Hz/73,728 = 50000Hz  ->  1/50000Hz = 20us (Compare Match 
Interrupt)
  OCR2  = 37;

sei(); // Interrupt erlauben
__________________________________

Meinst du das passt soweit?oder sollte ich doch lieber auf eien externen 
interrupt gehen?

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Das gibt eine Wickelvorrichtung für eine Seiltrommel ;).
> Das Seil soll vor der Trommel hin und her gefahren werden,

Kopfklatsch.
Natürlich!
Alles klar. Damit macht das natürlich Sinn.

von ömer (Gast)


Lesenswert?

hehe.....

Ich hätte das vielleicht auch vorher mal erwähnen können ;).

Was hälst du von dem Gedanken der Timersteuerung. Ich habe mal den Timer 
so laufen lassen mit der Drehgeberauswertung vom ersten Post. Es 
funktioniert auch alles wunderbar, aber wieder mal nur in den niedrigen 
Drehzahlen. Danach ist wieder das Problem des Hin und Her wackelns....

von Karl H. (kbuchegg)


Lesenswert?

> - Trommel dreht maximal mit 1333U/min=22,2U/s
> - 22,2U/s * 1000 Impulse/U = 22,2*10^3Impulse/s
> - Alle 45us bekomme ich ein Impuls
> - Doppelte Abtastung ergibt ca. 20us ein overflow oder compare match

Der Rechengang sieht soweit richtig aus.
Dreht die Trommel tatsächlich 22 mal in der Sekunde?

(Entschuldige meine Ungläubigkeit, aber ich kann mir das nur schwer 
vorstellen, dass man ein Seil so schnell aufwickeln muss. Ich nehm 
einfach mal an, dass da eine Getriebestufe zwischen Trommel und Encode 
sitzt und sich nur der Encode so schnell dreht. Wobei: Da frag ich mich 
- wozu soll der Encoder 1000 Positionen innerhalb einer Umdrehung 
auflösen, wenn du die nachher sowieso wieder rausrechnest)

>  // Vergleichswert; TCNT2 vergleicht seinen Zähler mit OCR2 und
>  // 3686400Hz/73,728 = 50000Hz  ->  1/50000Hz = 20us (Compare Match
>  Interrupt)
>  OCR2  = 37;

37 ist schon heftig wenig. Das bedeutet nämlich im Umkehrschluss, dass 
deine ISR nicht mehr als diese 37 Takte dauern darf. Und das wird dann 
schon heftig eng! Bei einer ISR würde ich mal von so ca. 20 Takten 
alleine für den C-Grundaufsatz ausgehen.
Und dann arbeitet dein µC ausser der ISR praktisch nichts anderes mehr 
ab. Der verbringt mindestens 80% seiner Rechenzeit damit, den Encoder zu 
überwachen.

Stell doch mal die 1000 bei der Encode Auflösung in Frage. Wozu brauchst 
du die denn überhaupt? Mit deinen Zahlen drängt sich der Schluss auf, 
dass du sowieso nur ganze Umdrehungen feststellen willst (was ich aus 
dem Bauch heraus logisch finden würde - so genau muss man ein Seil 
sicherlich nicht führen, dass es sich sauber aufwickelt). Dazu braucht 
es aber keinen 1000-er Encoder. Im Grunde würde es ein 1-er Enocder auch 
tun :-), was dein Timing (und damit den µC) gleich mal um einen Faktor 
1000 entlastet. Und über einen OCR Wert von 37000, da gähnt der µC nur 
noch lächelnd.

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:

> funktioniert auch alles wunderbar, aber wieder mal nur in den niedrigen
> Drehzahlen. Danach ist wieder das Problem des Hin und Her wackelns....

Ohne jetzt das genau ausgemessen zu haben.

Deine 37 Takte, die du der ISR erlaubst sind zu wenig. Da kommt die ISR 
nicht mit und ab einer Drehzahl gehen dann Pegelwechsel vom Encoder 
verloren. Dein Programm detektiert dann plötzlich eine Bewegung in die 
andere Richtung.

Kann man sich ein bißchen so vorstellen, wie das berühmte Wild-West 
Wagenrad im Fernsehen. Bei bestimmten Wagen-Geschwindigkeiten sieht es 
plötzlich so aus, als ob das Rad langsamer wird bzw. überhaupt in die 
andere Richtung dreht, obwohl es das in der Realität natürlich nicht 
tut.

von ömer (Gast)


Lesenswert?

Sorry hatte es vergessen zu verbessern. Aber anhand der rechnung kann 
man ja erkennen, dass ich ca. 74 als Vergleichswert erechnet hatte.

>  // Vergleichswert; TCNT2 vergleicht seinen Zähler mit OCR2 und
>  // 3686400Hz/73,728 = 50000Hz  ->  1/50000Hz = 20us (Compare Match
>  Interrupt)
>  OCR2  = 74;

Ok, also liegt es daran, dass meine ISR zu häufig aufgerufen wird und 
die restliche Abarbeitung dann ein wenig hinten dran steht.

Es ist richtig, dass ich keinen Encoder brauche der 1000 Impulse 
liefert. Das ist absolut zu viel und wird auch nicht verrechnet. 
Eigentlich pulsiere ich nur alle 1000 Impulse um eine volle Umdrehung 
realisieren zu können.

Ich könnte im Gedankengang dann auch den Nulldurchgang als externe 
Interrupt Quelle nehmen. Leider muss ich aber dann trotzdem die 
Drehrichtung bestimmen können.

Der Motor dreht mit ca. 4000U/min. Das Getriebe treibt die Trommel dann 
mit ca. 1333U/min mit einer kleinen Überlast an. Es soll eine 
Verfahrgeschwindigkeit von ca. 80km/h am Seil bereit stehen, deshalb ist 
die Drehzahl an der Trommel so hoch.

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Sorry hatte es vergessen zu verbessern. Aber anhand der rechnung kann
> man ja erkennen, dass ich ca. 74 als Vergleichswert erechnet hatte.
>
>>  // Vergleichswert; TCNT2 vergleicht seinen Zähler mit OCR2 und
>>  // 3686400Hz/73,728 = 50000Hz  ->  1/50000Hz = 20us (Compare Match
>>  Interrupt)
>>  OCR2  = 74;
>
> Ok, also liegt es daran, dass meine ISR zu häufig aufgerufen wird und
> die restliche Abarbeitung dann ein wenig hinten dran steht.

Das würde ich mal als Arbeitshypothese sehen.
Wenn sich im Programm sonst nichts mehr findet, wäre das für mich (ehe 
ich nachmesse) der plausibelste Grund.

Es ist meiner Meinung nach eine Kombination aus:
* ISR zu häufig
* ISR nicht schnell genug
* aber vor allen Dingen resultiert das daraus, dass sich die 
Encoderpegel zu schnell verändern.

Die ersten beiden Gründe ist eine direkte Folge aus dem letzten. Schraub 
die Änderungsrate runter und die Limits für die ersten beiden wachsen 
ganz von alleine in handhabbare Bereiche.

mit 2 LED, die je nach festgestellter Encoderdrehrichtung aufleuchten 
müsste man das eigentlich rauskriegen. Wenn die beide gemeinsam 
leuchten, dann behauptet die Encoderauswertung laufend, dass sich die 
Drehrichtung geändert hat.


> Ich könnte im Gedankengang dann auch den Nulldurchgang als externe
> Interrupt Quelle nehmen. Leider muss ich aber dann trotzdem die
> Drehrichtung bestimmen können.

Ich finde den Encoder als grundsätzliche einfachste Lösung schon ok. Nur 
halt nicht mit 1000 Schritten pro Drehung. Die gibts ja auch mit 
weniger.

> Verfahrgeschwindigkeit von ca. 80km/h am Seil bereit stehen,
<durch die Zähne pfeif>

von Karl H. (kbuchegg)


Lesenswert?

> 3686400Hz

3.6 Mhz

Welchen µC benutzt du ?

Kann der interne 8Mhz?

Wenn ja, dreh doch mal den Takt auf diese 8Mhz (F_CPU und OCR Wert 
anpassen nicht vergessen). Das müsste die Grenzdrehzahl in die Höhe 
treiben. Dann siehst du auch unmittelbar, ob du ein Timingproblem hast.

von ömer (Gast)


Lesenswert?

Also die Drehrichtung habe ich auf meinem Board schon mit LED´s am 
Anfang erkannt und konnte die auch anzeigen lassen.

Der Atmega auf meinen MyAvr MK2 board läuft anscheinend mit einem 
externen Quarz von 3,6MHz. (Also es steht zudmindest auf dem Quarz)

Ich habe am Anfang des Projektes auch schon versucht die Fuses zu 
ändern. Leider habe ich bereits 2 von den Atmega8 falsch gefused. Ich 
bin mir unsicher, weil ich nicht weiß wie man das macht.

Der Compiler von MYAVR kann das direkt im Programm. Ich habe mal den 
Status ausgelesen. und unten angefügt. Alles wasaktiviert ist, ist mit 
einem (-) versehen.
D.h. im Low Fuse: "Brown-out detection level at VCC=2.7 V" und "Ext. 
Crystal/Resonator High Freq.; Start-up time: 16K CK + 64 ms".

Im High Fuse: "Serial program downloading (SPI) enabled" und "Boot Flash 
section size=1024 words Boot start address=0x0C00; default value".

Im Lock BIT:"Mode 1: No memory lock features enabled", "Application 
Protection Mode 1: No lock on SPM and LPM in Application Section" und 
"Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader 
Section".

_______________________________________________
*LOW-FUSE:*
Brown-out detection level at VCC=4.0 V
- Brown-out detection level at VCC=2.7 V

Brown-out detection enabled

Ext. Clock; Start-up time: 6 CK + 0 ms
Ext. Clock; Start-up time: 6 CK + 4 ms
Ext. Clock; Start-up time: 6 CK + 64 ms
Int. RC Osc. 1 MHz; Start-up time: 6 CK + 0 ms
Int. RC Osc. 1 MHz; Start-up time: 6 CK + 4 ms
Int. RC Osc. 1 MHz; Start-up time: 6 CK + 64 ms; default value
Int. RC Osc. 2 MHz; Start-up time: 6 CK + 0 ms
Int. RC Osc. 2 MHz; Start-up time: 6 CK + 4 ms
Int. RC Osc. 2 MHz; Start-up time: 6 CK + 64 ms
Int. RC Osc. 4 MHz; Start-up time: 6 CK + 0 ms
Int. RC Osc. 4 MHz; Start-up time: 6 CK + 4 ms
Int. RC Osc. 4 MHz; Start-up time: 6 CK + 64 ms
Int. RC Osc. 8 MHz; Start-up time: 6 CK + 0 ms
Int. RC Osc. 8 MHz; Start-up time: 6 CK + 4 ms
Int. RC Osc. 8 MHz; Start-up time: 6 CK + 64 ms
Ext. RC Osc. - 0.9 MHz; Start-up time: 18 CK + 0 ms
Ext. RC Osc. - 0.9 MHz; Start-up time: 18 CK + 4 ms
Ext. RC Osc. - 0.9 MHz; Start-up time: 18 CK + 64 ms
Ext. RC Osc. - 0.9 MHz; Start-up time: 6 CK + 4 ms
Ext. RC Osc. 0.9 MHz - 3.0 MHz; Start-up time: 18 CK + 0 ms
Ext. RC Osc. 0.9 MHz - 3.0 MHz; Start-up time: 18 CK + 4 ms
Ext. RC Osc. 0.9 MHz - 3.0 MHz; Start-up time: 18 CK + 64 ms
Ext. RC Osc. 0.9 MHz - 3.0 MHz; Start-up time: 6 CK + 4 ms
Ext. RC Osc. 3.0 MHz - 8.0 MHz; Start-up time: 18 CK + 0 ms
Ext. RC Osc. 3.0 MHz - 8.0 MHz; Start-up time: 18 CK + 4 ms
Ext. RC Osc. 3.0 MHz - 8.0 MHz; Start-up time: 18 CK + 64 ms
Ext. RC Osc. 3.0 MHz - 8.0 MHz; Start-up time: 6 CK + 4 ms
Ext. RC Osc. 8.0 MHz - 12.0 MHz; Start-up time: 18 CK + 0 ms
Ext. RC Osc. 8.0 MHz - 12.0 MHz; Start-up time: 18 CK + 4 ms
Ext. RC Osc. 8.0 MHz - 12.0 MHz; Start-up time: 18 CK + 64 ms
Ext. RC Osc. 8.0 MHz - 12.0 MHz; Start-up time: 6 CK + 4 ms
Ext. Low-Freq. Crystal; Start-up time: 1K CK + 4 ms
Ext. Low-Freq. Crystal; Start-up time: 1K CK + 64 ms
Ext. Low-Freq. Crystal; Start-up time: 32K CK + 64 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 258 CK + 4 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 258 CK + 64 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 1K CK + 0 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 1K CK + 4 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 1K CK + 64 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 16K CK + 0 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 16K CK + 4 ms
Ext. Crystal/Resonator Low Freq.; Start-up time: 16K CK + 64 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 258 CK + 4 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 258 CK + 64 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 1K CK + 0 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 1K CK + 4 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 1K CK + 64 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 16K CK + 0 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 16K CK + 4 ms
Ext. Crystal/Resonator Medium Freq.; Start-up time: 16K CK + 64 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 258 CK + 4 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 258 CK + 64 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 1K CK + 0 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 1K CK + 4 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 1K CK + 64 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 0 ms
Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 4 ms
-Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 64 ms

Calibration
OscCal 0 (1.0 MHz):  0
OscCal 1 (2.0 MHz):  0
OscCal 2 (4.0 MHz):  0
OscCal 3 (8.0 MHz):  0
________________________________________________
*HIGH FUSE:*
Reset Disabled (Enable PC6 as i/o pin)

Watch-dog Timer always on

-Serial program downloading (SPI) enabled

Preserve EEPROM memory through the Chip Erase cycle

Boot Flash section size=128 words Boot start address=0x0F80
Boot Flash section size=256 words Boot start address=0x0F00
Boot Flash section size=512 words Boot start address=0x0E00
-Boot Flash section size=1024 words Boot start address=0x0C00; default 
value

Boot Reset vector Enabled (default address=0x0000)

CKOPT fuse (operation dependent of CKSEL fuses)
_____________________________________________________
*LOCK BITS:*
-Mode 1: No memory lock features enabled
Mode 2: Further programming disabled
Mode 3: Further programming and verification disabled

-Application Protection Mode 1: No lock on SPM and LPM in Application 
Section
Application Protection Mode 2: SPM prohibited in Application Section
Application Protection Mode 3: LPM and SPM prohibited in Application 
Section
Application Protection Mode 4: LPM prohibited in Application Section

-Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader 
Section
Boot Loader Protection Mode 2: SPM prohibited in Boot Loader Section
Boot Loader Protection Mode 3: LPM and SPM prohibited in Boot Loader 
Section
Boot Loader Protection Mode 4: LPM prohibited in Boot Loader Section

_____________________________________________________

Was ich am Anfang noch nicht versucht hatte, den Atmega mit internen 
8MHz zu betakten. Was muss ich aber dafür tun?

von ömer (Gast)


Lesenswert?

Uhh es ist doch ein langer Text geworden.
Ich habe den Atmega8-a-pu. Mit einem MYAvr MK2 Board. Auf dem externen 
Quarz steht 3,6864Mhz.

Im Folgenden Link sind die Fuses des Boards angegeben. Normalerweise 
kann ich sie doch dann nach dem Shema: "8 Mhz interner RC-Oscillator" 
einstellen.

http://projekte.myavr.de/index.php?sp=pages/kleiner_bascom_avr_kurs_fusebits_standard

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Also die Drehrichtung habe ich auf meinem Board schon mit LED´s am
> Anfang erkannt und konnte die auch anzeigen lassen.
>
> Der Atmega auf meinen MyAvr MK2 board läuft anscheinend mit einem
> externen Quarz von 3,6MHz. (Also es steht zudmindest auf dem Quarz)

Hmm.
Mit der Geschichte stehen die Chancen gut, dass der überhaupt nur mit 
1Mhz läuft!


> Der Compiler von MYAVR kann das direkt im Programm.

Der Compiler sicher nicht.
Welche Entwicklungsumgebung benutzt du?

Das ist sehr ungewöhnlich, wenn die die Fuses beim Brennen ändert.


> Was ich am Anfang noch nicht versucht hatte, den Atmega mit internen
> 8MHz zu betakten. Was muss ich aber dafür tun?

Das wäre diese Einstellung
> Int. RC Osc. 8 MHz; Start-up time: 6 CK + 64 ms

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Uhh es ist doch ein langer Text geworden.
> Ich habe den Atmega8-a-pu. Mit einem MYAvr MK2 Board. Auf dem externen
> Quarz steht 3,6864Mhz.

Das sagt nichts.

Du kannst in deinen VW-Käfer einen 2000 PS Motor reinlegen. Deswegen hat 
der Käfer trotzdem nur seine 60 PS (klassischer VW-Käfer).

Der Quarz muss auch aktiv sein bzw. der µC muss angewiesen werden, den 
Quarz zu verwenden!
Nur weil er da ist, heißt das noch lange nicht, dass er auch arbeitet.

von ömer (Gast)


Lesenswert?

Also ich benutze das MYAvr Workpad und kann mit diesem die Fuses 
auslesen und ändern. Ich kann nicht sagen mit welcher Frequenz der 
Momentan läuft. Ich kann es nur mit Hilfe des Programms auslesen, 
verändern und schreiben.

Ich kann gerne mal die Frequenz auf "Int. RC Osc. 8 MHz; Start-up time: 
6 CK + 64 ms" stellen.
Alle anderen Werte bleiben dabei ja gleich, oder?

Kann ich irgendwie die Frequenz heraus bekommen?

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Also ich benutze das MYAvr Workpad und kann mit diesem die Fuses
> auslesen und ändern. Ich kann nicht sagen mit welcher Frequenz der
> Momentan läuft.

Vergiss was ich gesagt habe.
Dein Log zeigt es ja

-Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 64 ms

Also wird auch der Quarz benutzt (allerdings ist ein 3.4 Mhz Quarz kein 
High Frequency. Die Grenze High/Low liegt bei ca. 8Mhz QUarz)


> Ich kann gerne mal die Frequenz auf "Int. RC Osc. 8 MHz; Start-up time:
> 6 CK + 64 ms" stellen.
> Alle anderen Werte bleiben dabei ja gleich, oder?

Ja.

> Kann ich irgendwie die Frequenz heraus bekommen?

Klar.
1
#define F_CPU 3686400UL
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
int main()
6
{
7
  ... Ausgangsport mit 1 LED auf Ausgang stellen
8
9
  while( 1 ) {
10
    Led einschalten
11
    _delay_ms( 1000 );
12
    Led aussschalten
13
    _delay_ms( 1000 );
14
  }
15
}

Wenn die F_CPU Angabe korrekt ist, dann ist die LED 1 Sekunde an / 1 
Sekunde aus.
Arbeitet der µC nur mit 1Mhz, dann sind die Zeiten länger. 3.6864 
Sekunden an / 3.6864 Sekunden aus.
Arbeitet er mit 8Mhz dann werden die Warteschleifen, die für 3.6Mhz 
berechnet wurden, zu schnell abgearbeitet und die Zeiten sind dann 
ungefähr 0.5 Sekunden an / 0.5 Sekunden aus.

Damit kann man optisch leicht kontrollieren, ob die Taktfrequenz mit 
F_CPU einigermassen übereinstimmt. Ob das dann 3.6Mhz oder 3.2Mhz sind, 
kann man so nicht sagen. Aber die Möglichkeiten bewegen sich ja nicht in 
diesem kleinen Rahmen. Zwischen 1Mhz, 3.6Mhz und 8Mhz (das sind die 
Möglichkeiten)  ist genug Unterschied, den man auch optisch an der 
Blinkzeit sieht.
Wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt, dann sid 
die 1000 Millisekunden auch tatsächlich 1 Sekunde an der LED.

von ömer (Gast)


Lesenswert?

Ich habe mit der Stoppuhr ca. 2 - 2,5sek gemessen.

Nach ändern der Fuse Bits auf 8MHz, bekomme ich ca. 1sek. heraus....

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Ich habe mit der Stoppuhr ca. 2 - 2,5sek gemessen.
>
> Nach ändern der Fuse Bits auf 8MHz, bekomme ich ca. 1sek. heraus....

wenn du F_CPU da schon auf 8000000UL gestellt hast, dann passt das. Der 
µC läuft mit Sicherheit auf 8Mhz :-)

Vergiss nicht auf den OCR2 Wert.
Rein rechnerisch ist der 160

Besser du schreibst das so im Programm

   OCR2 = F_CPU / 50000;

Dann rechnet ihn der COmpiler aus.

von ömer (Gast)


Lesenswert?

Nein nach Ändern der Fuse bits hatte ich Leider die Frequenz noch auf 
dem alten Wert.
Mit Änderung der Frequenz auf 8Mhz im Header bekomme ich knapp 3sek 
heraus.

von Karl H. (kbuchegg)


Lesenswert?

Hmm.

Zeig noch mal dein letztes Programm.
Wie hast du die Kopplung Encoder - Motorsteuerung gemacht?

Du darfst nicht von der Encoder-Auswertung direkt die Motorsteuerung 
aufrufen! Das muss in der Hauptschleife in main() passieren!

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Nein nach Ändern der Fuse bits hatte ich Leider die Frequenz noch auf
> dem alten Wert.
> Mit Änderung der Frequenz auf 8Mhz im Header bekomme ich knapp 3sek
> heraus.

Da hat dann beim Schreiben der Fusebits was nicht geklappt bzw stimmt 
nicht überein.

Aber ist ok. Bei einm Kaffee ist mir noch ein möglicher Fehler 
eingefallen, denn du gemachen haben könntest. 70 Takte ISR sind (im 
Gegensatz zu den 37) zwar noch nicht die Welt (150 wären besser), aber 
zumindest ist es nicht mehr sooooo knapp.

Belassen wirs bei den 3.6Mhz.
Mit den Fusebits kannst du ja auch mal experimentieren, wenn das Projekt 
läuft.


Zeig doch mal dein jetziges Programm. Ich würd gerne sehen, wie du die 
Encoder-Motor Kopplung gemacht hast.

von ömer (Gast)


Angehängte Dateien:

Lesenswert?

Ok, hier nochmal der Gesamte Code. Ich musste den zähler erstmal doch 
rechts und links laufen lassen, da ich Probleme hatte nur einen Zähler 
zu verwenden.

Nachdem Umstellen der FuseBits auf 8MHZ konnte ich eine Veränderung 
feststellen. Ich kann jetzt viel höher drehen, als zuvor.

Ich habe die Änderung der F_CPU mit rein genommen. Und dies auch in OCR2 
geändert.

von Karl H. (kbuchegg)


Lesenswert?

Probiers mal so
1
int main()
2
{
3
  int Position;
4
5
  ...
6
7
  while( 1 ) 
8
  {
9
    cli();
10
    Position += enc_delta;
11
    enc_delta = 0;
12
    sei();
13
14
    if( Position > 1000 )
15
    {
16
      Position = Position - 1000;
17
      MotorStepForward();
18
    }
19
    else if( Position < 0 )
20
    {
21
      Position = Position + 1000;
22
      MotorStepBackward();
23
    }
24
  }
25
}

von Karl H. (kbuchegg)


Lesenswert?

Du darfst nicht mit getrennten rechts/links Werten beim Encoder 
arbeiten.

Stell dir einfach vor, dass du mit dem Encoder die Umdrehungsnummer der 
Trommel steuerst. Wenn du ein positives enc_delta hast, dann erhöht sich 
diese Umdrehungsnummer, hast du ein negatives, dann wird sie kleiner. 
Dazu musst du einfach nur den enc_delta Wert auf die Umdrehungsnummer 
dazuaddieren, dann kommt das richtig raus.
Und wenn 1000 Umdrehungen beisammen sind, wird der Schrittmotor um 1 
weiter gestellt. Bzw. umgekehrt, wenn du 800 Umdrehungen in die eine 
Richtung gezählt hast und 900 in die andere, dann landest du irgendwann 
bei einer negativen Umdrehungsnummer und machst einen Schritt in die 
andere Richtung.

von Karl H. (kbuchegg)


Lesenswert?

Ach Shit.

Asymetriefehler
1
int main()
2
{
3
  int Position;
4
5
  ...
6
7
  while( 1 ) 
8
  {
9
    cli();
10
    Position += enc_delta;
11
    enc_delta = 0;
12
    sei();
13
14
    if( Position > 1000 - 1 )
15
    {
16
      Position = Position - 1000;
17
      MotorStepForward();
18
    }
19
    else if( Position < 0 )
20
    {
21
      Position = Position + 1000;
22
      MotorStepBackward();
23
    }
24
  }
25
}

Von 0 bis 999 sind 1000 Schritte. D.h. der Foward Step muss bereits bei 
einem Zählerstand von > 999 erfolgen.

von ömer (Gast)


Lesenswert?

Jetzt kann ich Beispielsweise 5 Umdrehungen drehen und der Motor läuft 
noch eine Zeit lang nach, obwohl der Ink.Geber schon gestoppt ist.

Manchmal kommt es auch vor, dass ich rechts drehe und der motor macht 2 
rechtsdrehung, dann aber ca. 6 in die andere Richtung. Alles ohne 
Drehung des Ink-Gebers

von Karl H. (kbuchegg)


Lesenswert?

ömer schrieb:
> Jetzt kann ich Beispielsweise 5 Umdrehungen drehen und der Motor läuft
> noch eine Zeit lang nach, obwohl der Ink.Geber schon gestoppt ist.

?

> Manchmal kommt es auch vor, dass ich rechts drehe und der motor macht 2
> rechtsdrehung, dann aber ca. 6 in die andere Richtung. Alles ohne
> Drehung des Ink-Gebers

Nochmal das jetzige Programm bitte.

von ömer (Gast)


Angehängte Dateien:

Lesenswert?

.....

von Karl H. (kbuchegg)


Lesenswert?

Ah.
Mein Fehler.
Ich hab mich im Kopf mit enc_delta vertan.

von Karl H. (kbuchegg)


Lesenswert?

Gib mir ein paar Minuten, das muss ich jetzt selbst erst mal auseinander 
sortieren, wie das funktioniert.

von ömer (Gast)


Lesenswert?

Ja kein Problem.

Ich habe den Code von dir auch noch nicht verstanden. Habe ihn nur mal 
eingefügt....

von Karl H. (kbuchegg)


Lesenswert?

Hmm
Das sollte eigentlich schon stimmen.

Einziger Fehler

  int Position = 0;

der muss auf 0 gesetzt werden, sonst fängt die Logik mit irgendeinem 
Wert an.


Wenn du testest, testest du das erst mal langsam oder gehst du gleich 
Full Speed drauf?

von ömer (Gast)


Lesenswert?

Also langsame Drehzahlen macht der Code ganz gut.
Die hohen Drehzahlen lassen in erheblich nachlaufen, trotz das der Ink 
Geber schon steht.
Problem ist bei einem falschen Ausgangswert: rechts anstatt links 
Drehung, wird das Nachlaufen in eine andere Richtung zum Problem....

von Karl H. (kbuchegg)


Lesenswert?

Hmm
Deine Encoderauswertung in der ISR sieht im Detail anders aus als diese 
hier
Drehgeber

von Karl H. (kbuchegg)


Lesenswert?

Geh mal mit den
1
void MakeStep( uint8_t MotorDir )
2
{
3
  if( MotorDir == TO_RIGHT )
4
  {
5
    PORTC |= ( 1 << PC1 );
6
    _delay_ms(60);
7
    PORTC &=~ (1 << PC1);
8
    _delay_ms(60);    
9
  }
10
  else
11
  {
12
    PORTC |= ( 1 << PC2 );
13
    _delay_ms(60);
14
    PORTC &=~ (1<<PC2);
15
    _delay_ms(60);    
16
  }
17
}

60 Millisekunden runter. Der Teil dauert 120 Millisekunden, das bedeutet 
aber auch, dass der Schrittmotor schon mal prinzipiell nicht mehr als 8 
Schritte in der Sekunde machen kann. Ab einer Drehzahl von 500U/min 
kommt der also schon mal prinzipiell nicht mehr mit.

von Karl H. (kbuchegg)


Lesenswert?

Der Code zählt nämlich prinzipiell die Anzahl der Umdrehungen der 
Trommel. Und genauso viele Schrittmotorsteps generiert er auch. Wenn die 
Trommel 100 Umdrehungen macht und dafür 2 Sekunden braucht, der 
SChrittmotor für 100 Steps aber 5 Sekunden braucht, dann hinkt der 
natürlich nach.

(Werte jetzt erfunden, es geht ums Problemprinzip)

Aber die unmotivierte Richtungsänderung erklärt das auch noch nicht. Es 
sei denn, der int Position läuft über. Hmmm

von ömer (Gast)


Lesenswert?

Vermutlich macht das deine Erfahrung aus???? ;)

Ich freu mich, denn an dem _delay hats gelegen. Deshalb sollte man sowas 
ja auch nicht machen, oder besser vermeiden.

Ich werde das ganze aber die nä. Woche noch ausgiebiger testen könne....

von Karl H. (kbuchegg)


Lesenswert?

Super.

Und ich hab schon hin und her überlegt, ob da möglicherweise in der 
Encoderauswertung was falsch sein könnte oder vielleicht gar ein 
Hardwareproblem den Eincoder-Eingängen irgendwas vorgaukelt :-)

von ömer (Gast)


Lesenswert?

Nein, du kannst dich erstmal beruhigen. Ich werde dir dann nächste Woche 
noch eine Rückmeldung geben, wie es funktioniert hat....
Schönes Weekend!

von ömer (Gast)


Lesenswert?

Hi,

ich melde mich nochmal.

Habe das Programm jetzt in der Praxis mal getestet. Musste dort 
feststellen, dass ich die Phasenerkennung bis 4000 zählen muss um eine 
Umdrehung des Ink.-Gebers zu erkennen.
Ich habe damit kein Problem, es funktioniert ja. Aber dennoch würde mich 
interessieren, warum ein Geber mit 1000 Impulsen mit meinem Programm 
4000 Zählungen braucht?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.