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.
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.
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!
ö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.
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.
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_tMotorDir=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
voidMotorStepForward()
16
{
17
uint8_tStepDir;// 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
intmain(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!
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
voidMakeStep(uint8_tDirection)
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
voidMotorStepForward()
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
voidMakeStepBackward()
32
{
33
....dubistdrann
34
....amEndewirddannwiederMakeStepaufgerufen
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)
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.
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.
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.
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.)
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.
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??
Ö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....
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 );
Ö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!
Ö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.
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.
Ö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.
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....
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.
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?!
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.....
// 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
voidMotorStepForward()
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.
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.
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.
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.
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)
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
___________________________________________________
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.
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.
Sorry. Konnte nicht früher
Der springende Punkt liegt hier ...
1
voidMotorStepForward()
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
voidMotorStepForward()
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
voidMotorStepForward()
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
voidMotorStepBackward()
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
voidChekForTurn(uint8_tDirRight,uint8_tDirLeft)
2
{
3
if((Endschalter_Rechts&&MotorDir==DirRight)||
4
(Endschalter_Links&&MotorDir==DirLeft))
5
MotorDir=1-MotorDir;
6
}
7
8
voidMotorStepForward()
9
{
10
CheckForTuen(TO_RIGHT,TO_LEFT);
11
MakeStep(MotorDir);
12
}
13
14
voidMotorStepBackward()
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.
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.
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);
}
ö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.
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.
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?
ö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.
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....
> - 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.
ö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.
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.
ö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>
> 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.
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?
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
ö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
ö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.
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?
ö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
intmain()
6
{
7
...Ausgangsportmit1LEDaufAusgangstellen
8
9
while(1){
10
Ledeinschalten
11
_delay_ms(1000);
12
Ledaussschalten
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.
ö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.
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.
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!
ö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.
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.
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.
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
ö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.
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?
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....
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.
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
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....
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 :-)
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?