Go Down

Topic: Projekt: Schrittmotor (Read 13316 times) previous topic - next topic

sonyfuchs

Guten Abend liebe Gemeinde,
ich bin kürzlich stolzer Besitzer eines Arduino Mega geworden und wollte hier einen Schrittmotor ansteuern. Genauer genommen einen Schrittmotortreiber, der zwischen Schrittmotor und Arduino hängt.
Leider sind meine, 10 Jahre alten, C-Kentnisse stark eingerostet.
Das Programm so wie es ist lässt den Schrittmotor laufen. Füge ich die Zeilen mit den Kommentaren ein funktioniert es nicht mehr. Ich möchte dass wenn ich in die Konsole 1 eingebe die CLK generiert wird und wenn ich eine 0 eingebe, dass Programm anhält.

Code: [Select]
boolean CLK =13; //Schrittgeschwindigkeit
boolean DIR =12; //Drehrichtung
boolean NEN = 9; //NOT ENABLE
boolean HAL = 8; //1/2
boolean QUA = 7; //1/4
boolean x;

void setup()
{
 pinMode(CLK, OUTPUT);
 pinMode(DIR, OUTPUT);
 pinMode(NEN, OUTPUT);
 pinMode(HAL, OUTPUT);
 pinMode(QUA, OUTPUT);
 Serial.begin(9600);
 Serial.println("Test");
}

void loop()
{
 //x = Serial.read();
 //while(x==1)
 //{
   digitalWrite(CLK, HIGH); // CLK einschalten
   delay(5);
   digitalWrite(CLK, LOW); // CLK ausschalten
   delay(5);  
  // continue;
 //}
}

Wie setze ich "Serial.read" richtig ein?

Serenifly

#1
Jul 25, 2013, 09:24 pm Last Edit: Jul 25, 2013, 09:59 pm by Serenifly Reason: 1
So macht das keinen Sinn. Du musst Serial.read() innerhalb der while-Schleife einlesen. Und continue macht hier auch nichts. Damit beendet man die aktuelle Iteration und geht zur nächsten.

Code: [Select]
boolean CLK =13;
boolean? Was du da willst ist ein int.

So ist es meiner Ansicht nach intuitiver:
Code: [Select]

volatile bool run = false;

loop()
{
   if(run)
   {
       digitalWrite(CLK, HIGH); // CLK einschalten
       delay(5);
      digitalWrite(CLK, LOW); // CLK ausschalten
      delay(5);
  }
}


void serialEvent()
{
     x = Serial.read();
     if(x == '0')
     {
       run = false;
     }
     else(if x == '1')
       run = true;
}


Da bool sollte hier eigentlich auch run = Serial.read() funktionieren, aber mit der if-Abfrage bist du sicherer wenn mal was anderes geschickt wird.

Oder ohne Event-Handler:
Code: [Select]

loop()
{
    x = Serial.read();
    while(x == '1')
    {
       digitalWrite(CLK, HIGH); // CLK einschalten
       delay(5);
       digitalWrite(CLK, LOW); // CLK ausschalten
       delay(5);  
       
       x = Serial.read();
       if(x == '0')
          break;
    }
}


Der EventHandler ist aber besser, da du damit nicht ständig abfragen musst ob an der Schnittstelle etwas angekommen ist.

jurs


Ich möchte dass wenn ich in die Konsole 1 eingebe die CLK generiert wird und wenn ich eine 0 eingebe, dass Programm anhält.


Da solltest Dir erstmal klar darüber werden, was eigentlich gesendet wird, wenn Du "1" oder "0" eingibst und sendest.

Der serielle Monitor von Arduino sendet immer Zeilenweise. Jede Zeile wird mit den beiden Steuerzeichen CR (Carriage Return, ASCII-13) und LF (Linefeed, ASCII-10) abgeschlossen.

Das gesendete Zeichen wird in ASCII-Zeichencodierung gesendet, also für "0" eine ASCII-48 und für "1" eine ASCII 49.

D.h. wenn Du "1" sendest, sendest Du tatsächlich: 49 13 10
Und wenn Du "0" sendest, sendest Du tatsächlich: 48 13 10

Du kannst z.B. die Daten laufend auslesen und im Fall, dass 49 und 48 gelesen werden, entsprechend reagieren:

Code: [Select]

void loop()
{ char x;
 static boolean clkEin=false;
 x = Serial.read();
 if (x==49) clkEin=true;
 else if (x==48) clkEin=false;
 else ; // ist egal
 if (clkEin)
    {
       digitalWrite(CLK, HIGH); // CLK einschalten
       delay(5);
       digitalWrite(CLK, LOW); // CLK ausschalten
       delay(5);  
    }
}

sonyfuchs

ich musste es leicht abändern weil er n paar sachen nicht kannte, aber läuft trotzdem nicht   :(
Code: [Select]
int CLK =13; //Schrittgeschwindigkeit
int DIR =12; //Drehrichtung
int NEN = 9; //NOT ENABLE
int HAL = 8; //1/2
int QUA = 7; //1/4
boolean x;
boolean run = false;

void setup()
{
  pinMode(CLK, OUTPUT);
  pinMode(DIR, OUTPUT);
  pinMode(NEN, OUTPUT);
  pinMode(HAL, OUTPUT);
  pinMode(QUA, OUTPUT);
  Serial.begin(9600);
  Serial.println("Test");
}

void loop()
{
  while(run)
  {
    digitalWrite(CLK, HIGH); // CLK einschalten
    delay(5);
    digitalWrite(CLK, LOW); // CLK ausschalten
    delay(5);
  }
}


void Serialevent()
{
      x = Serial.read();
      if(x == 0)
      {
        run = false;
      }
      else if (x == 1)
        run = true;
}


ich muss noch anmerken dass "Serialevent" schwarz geschrieben wird, also nicht als besondere eingabe erkannt wird wie "loop"
^_^ ich weiß dass hört sich für die meisten lächerlich an. bitte habt nachsicht

Serenifly

#4
Jul 25, 2013, 09:44 pm Last Edit: Jul 25, 2013, 09:56 pm by Serenifly Reason: 1
Wie jurs sagte, noch vergessen, dass da Characters geschickt werden. Also entweder auf den ASCII-Wert abfragen, oder '0' und '1' (die Anführungsstriche sagen, dass es chars sind und nicht Zahlen). Und x dann als char und nicht als bool.

Und "run" sicherheitshalber als volatile definieren.

Das kommt auch drauf an was man da sonst noch macht. Wenn da später noch anderes Zeug dazu kommt, das in der loop läuft ist da natürlich "if" sinnvoller als "while", sonst steckt man ständig in der while-Schleife drin.

Und gerade noch gemerkt, dass man bei serialEvent, sowieso "if" braucht, da der eventHandler zwischen den loop-Iterationen ausgeführt wird. Oops. :)

sonyfuchs

vielen dank jurs, dass hat funktioniert. werd mir dass mal gleich richtig ansehen

jurs


vielen dank jurs, dass hat funktioniert. werd mir dass mal gleich richtig ansehen


Nur zu! Und wie erwähnt, kannst Du statt auf die ASCII-Codes der Zeichen auch direkt auf die Zeichen prüfen, d.h.

 if (x==49) clkEin=true;
 else if (x==48) clkEin=false;
ist völlig gleichbedeutend mit:
 if (x=='1') clkEin=true;
 else if (x=='0') clkEin=false;


sonyfuchs

#7
Jul 27, 2013, 11:16 am Last Edit: Jul 27, 2013, 11:46 am by sonyfuchs Reason: 1
so, ich hab mal n bischen rumprobiert und hab jetzt n paar fragen, aber erstmal der aktuelle Code:
Code: [Select]
/*
 Der Motor ist bei 00 ausgeschaltet,
 mit 01 und 10 ändert man die Drehrichtung.
*/

int CLK =13; //Schrittgeschwindigkeit
int DIR =12; //Drehrichtung
int NEN = 9; //NOT ENABLE
int HAL = 8; //1/2
int QUA = 7; //1/4

char x;
//static boolean clkEin;

void setup()
{
 pinMode(CLK, OUTPUT);
 pinMode(DIR, OUTPUT);
 pinMode(NEN, OUTPUT);
 pinMode(HAL, OUTPUT);
 pinMode(QUA, OUTPUT);
 Serial.begin(9600);
 Serial.println("Test");
}

void loop()
{
 static boolean clkEin=false;
 x = Serial.read();
 if (x=='10')
 {
   digitalWrite(DIR, LOW);
   digitalWrite(NEN, LOW);
   clkEin=true;
 }
 else if (x=='00')
 {
   digitalWrite(NEN, HIGH);
   clkEin=false;
 }
 else if (x=='01')
 {
   digitalWrite(DIR, HIGH);
   digitalWrite(NEN, LOW);
   clkEin=true;
 }
 if (clkEin)
 {
   digitalWrite(CLK, HIGH); // CLK einschalten
   delay(5);
   digitalWrite(CLK, LOW); // CLK ausschalten
   delay(5);  
 }
}


so nun die Fragen:
1. wenn ich die Zeile
Code: [Select]
static boolean clkEin=false;
aus loop zu den anderen Deklarationen nach oben verschiebe, läuft das Programm nicht mehr. Warum?
2. wo ist der unterschied zwischen static boolean und boolean. bei abänderung von static boolean zu boolean funktioniert das Programm auch nicht.
3. aus 48 und 49, '0' und '1' zu machen hat funktioniert. Jetzt hab ich das Programm um die Drehrichtung erweitert und mit '10' '00' und '01' läuft es nicht mehr, wenn ich aber '0' '1' und '2' mache schon. Woran liegt dass?
4. ich möchte dass NEN grundsätzlich High ist wenn nicht 1 oder 2 eingegeben wurde. schreib ich es aber for die if abfrage startet der Motor nicht mehr. Wie kann ich dass abändern?

Serenifly

#8
Jul 27, 2013, 11:57 am Last Edit: Jul 27, 2013, 12:50 pm by Serenifly Reason: 1
Quote
wo ist der unterschied zwischen static boolean und boolean.

static hat zwei Funktionen:
1.) In Klassen verwendet, ist die Variable für alle Instanzen gleich und existiert nicht getrennt für jede Instanz
2.) In Methoden behält sie ihren Wert von einem Aufruf zum nächsten und wird nicht jedes mal neu initialisiert. Die Initialisierung geschieht nur beim ersten Aufruf.

Hier geht es um den zweiten Fall

Wenn du innerhalb der loop das static weg lässt wird die Variable bei jedem Durchlauf auf false zurückgesetzt. Daher wird dann der Clock-Impuls für jedes Senden nur einmal gemacht. Das sollte aber auch mit der Deklaration außerhalb ohne static funktionieren.

Globale Variable nimmt man gewöhnlich nur wenn diese wirklich im ganzen Programm gelten müssen, z.B. die Pin-Belegung. Sonst hält man deren Geltungsbereich (Scope) so klein wie möglich. "char x" z.B. muss nicht global sein und wird in jeder loop-Iteration neu eingelesen. "clkEin" muss seinen Wert von einem loop-Aufruf zum nächsten behalten, aber da gibt es eben static als Alternative zur globalen Deklaration. Es ist aber nicht grundlegend falsch das global zu machen, wenn es für dich verständlicher ist.

Quote

3. aus 48 und 49, '0' und '1' zu machen hat funktioniert. Jetzt hab ich das Programm um die Drehrichtung erweitert und es läuft nicht mehr. Woran liegt dass?

char ist ein Zeichen. Du kannst nicht 'xx" in einen char schreiben. Benutze dafür zwei getrennte chars, die du nacheinander einließt.

Code: [Select]

char x = Serial.read();
char y = Serial.read();

if(x == '1' && y == '0')   //am besten hier mal bessere Variablen-Namen nehmen
{
}



Da ich mich gewundert habe wieso da der Compiler nicht meckert, habe ich das mal in Visual Studio ausprobiert. Wenn man das macht:
int character = '00';
schreibt er "0011 0000 0011 0000" rein. Der rechte Character steht dabei im niederwertigem Byte, wobei man das hier nicht sieht, da beide gleich sind.

"0011 0000" ist 48 = '0'. Wenn man das einem char zuweist, kommt eine Warnung "warning C4305: 'initializing' : truncation from 'int' to 'char'", und er weist den rechten Character zu und ignoriert den Rest. Es kompiliert also, aber durch die implizite Konversion gehen Daten verloren.

sonyfuchs

danke erstmal für die Erklärung des Begriffs "static". leider funktioniert es nicht wenn ich es global versuche.
Hab jetzt versucht mit einem zweiten char : y die zweite Zahl einzuladen:
Code: [Select]
/*
  Der Motor ist bei 00 ausgeschaltet,
  mit 01 und 10 ändert man die Drehrichtung.
*/

int CLK =13; //Schrittgeschwindigkeit
int DIR =12; //Drehrichtung
int NEN = 9; //NOT ENABLE
int HAL = 8; //1/2
int QUA = 7; //1/4

char x;
char y;
//static boolean clkEin;

void setup()
{
  pinMode(CLK, OUTPUT);
  pinMode(DIR, OUTPUT);
  pinMode(NEN, OUTPUT);
  pinMode(HAL, OUTPUT);
  pinMode(QUA, OUTPUT);
  Serial.begin(9600);
  Serial.println("Test");
}

void loop()
{
  static boolean clkEin=false;
  x,y = Serial.read();
  //y = Serial.read();
  if (x == '1' && y == '0')
  {
    digitalWrite(DIR, LOW);
    digitalWrite(NEN, LOW);
    clkEin=true;
  }
  else if (x == '0' && y == '0')
  {
    digitalWrite(NEN, HIGH);
    clkEin=false;
  }
  else if (x == '1' && y == '1')
  {
    digitalWrite(DIR, HIGH);
    digitalWrite(NEN, LOW);
    clkEin=true;
  }
  if (clkEin)
  {
    digitalWrite(CLK, HIGH); // CLK einschalten
    delay(5);
    digitalWrite(CLK, LOW); // CLK ausschalten
    delay(5); 
  }
}


leider funktioniert dass nicht. der Motor springt weder auf 1 0 00 01 10 an.
wie kann ich NEN so festlegen, dass wenn noch keine Angabe gemacht wurde er Grundsätzlich HIGH ist?

Serenifly

#10
Jul 27, 2013, 01:27 pm Last Edit: Jul 27, 2013, 02:10 pm by Serenifly Reason: 1
Code: [Select]

x = Serial.read();
y = Serial.read();


"x,y = Serial.read()" geht nicht. Du willst zwei Zeichen einlesen, also musst du auch zweimal read() machen.

Du darfst das nicht mit der Initialisierung von Variablen verwechseln. Und wenn du "int x, y = 3" machst, schreibt er auch nur 3 in y, aber x bleibt 0 (es gibt auch Compiler, die keinen default value setzen was eigentlich Standard in C ist)! Wenn du dir über solche Feinheiten nicht im Klaren bist, gehe lieber auf Nummer sicher und schreibe es gleich ausführlich. Da ist der Code länger, aber er funktioniert auch.

Du musst dann im Serial Monitor zwei Zeichen direkt hintereinander schicken. Also "00" und dann senden.

Weiß nicht wieso das nicht gehen sollte wenn es korrekt programmiert ist.

Du kannst ja mal das nach dem Einlesen machen:
Code: [Select]

Serial.print("x: ");
Serial.print(x);
Serial.print(" - y: ");
Serial.println(y);


Dann schickt er dir die empfangen Zeichen sofort wieder zurück und du hast eine Kontrolle. Das ist eine Art Primitiv-Debugging. Damit kannst du auch überprüfen wo dein Programm steht, wenn du an bestimmten Stellen Ausgaben hinzufügst.

Wenn es geht musst du das aber wieder entfernen.

Quote

wie kann ich NEN so festlegen, dass wenn noch keine Angabe gemacht wurde er Grundsätzlich HIGH ist?

Einfach in Setup den Ausgang auf HIGH setzen.

sonyfuchs

#11
Jul 28, 2013, 12:26 pm Last Edit: Jul 28, 2013, 12:31 pm by sonyfuchs Reason: 1
so, jetzt funktioniert die eingabe von '00' '10' etc.
Code: [Select]
/*
 Der Motor ist bei 00 ausgeschaltet,
 mit 01 und 10 ändert man die Drehrichtung.
*/

int CLK =13; //Schrittgeschwindigkeit
int DIR =12; //Drehrichtung
int NEN = 9; //NOT ENABLE
int HAL = 8; //1/2
int QUA = 7; //1/4

char x;
char y;

void setup()
{
 pinMode(CLK, OUTPUT);
 pinMode(DIR, OUTPUT);
 pinMode(NEN, OUTPUT);
 pinMode(HAL, OUTPUT);
 pinMode(QUA, OUTPUT);
 Serial.begin(9600);
 Serial.println("Test");
 digitalWrite(NEN, HIGH);
}

void loop()
{
 static boolean clkEin=false;
 x = Serial.read();
 y = Serial.read();
 
 Serial.print("x: ");
 Serial.print(x);
 Serial.print(" - y: ");
 Serial.println(y);
 
 if (x == '1' && y == '0')
 {
   digitalWrite(DIR, LOW);
   digitalWrite(NEN, LOW);
   clkEin=true;
 }
 else if (x == '0' && y == '0')
 {
   digitalWrite(NEN, HIGH);
   clkEin=false;
 }
 else if (x == '1' && y == '1')
 {
   digitalWrite(DIR, HIGH);
   digitalWrite(NEN, LOW);
   clkEin=true;
 }
 if (clkEin)
 {
   digitalWrite(CLK, HIGH); // CLK einschalten
   delay(5);
   digitalWrite(CLK, LOW); // CLK ausschalten
   delay(5);  
 }
}


als ausgabe gibt er mir nur ein unverständliches:
Test
x: ÿ - y: ÿ
x: ÿ - y: ÿ
x: ÿ - y: ÿ
x: ÿ - y: ÿ
... usw.

ich glaub NEN ist trotzdem nicht von Anfang an auf High
wenn ich
Serial.println(NEN);
eingebe, gibt er mir 9 also den pin aus.
wie frag ich nach low und high ab?

jurs


als ausgabe gibt er mir nur ein unverständliches:
Test
x: ÿ - y: ÿ
x: ÿ - y: ÿ
x: ÿ - y: ÿ
x: ÿ - y: ÿ
... usw.


Du hast immer noch nicht die Funktionsweise der seriellen Schnittstelle und der dazugehörenden Library verstanden: Wenn kein Zeichen im Eingangspuffer der Schnittstelle vorhanden ist und Du liest die Serielle Schnittstelle aus mit:
x = Serial.read();
y = Serial.read();
dann bekommst Du von der read() Funktion immer einen Error-Code zurück. Den kannst Du printen wie Du lustig bist und es bleibt immer der Error-Code. Wenn Du auslesen möchtest was übertragen wurde, dann mußt Du immer zuerst mit "Serial.available()" prüfen, ob überhaupt ein Zeichen zum Lesen im Eingangspuffer vorhanden ist. Nur wenn ein Zeichen im Eingangspuffer drin ist, kannst Du den Wert dieses Zeichens aus dem Eingangspuffer auslesen. Sonst wird immer nur der Error-Code geliefert, wenn Du aus dem leeren Eingangspuffer liest.


ich glaub NEN ist trotzdem nicht von Anfang an auf High
wenn ich
Serial.println(NEN);
eingebe, gibt er mir 9 also den pin aus.
wie frag ich nach low und high ab?


Ja, logisch:
int NEN = 9;
Serial.println(NEN);
wird immer "9" ausgeben, solange NEN den wert "9" hat.

Wenn Du den Wert des Pins 9 (NEN) auslesen und ausgeben möchtest, dann brauchst Du digitalRead, und Du gibst den Wert von digitalRead(NEN) aus:
Serial.println(digitalRead(NEN));

Serenifly

#13
Jul 28, 2013, 12:50 pm Last Edit: Jul 28, 2013, 01:13 pm by Serenifly Reason: 1
Das war mein Fehler. Sorry :)

Das 'ÿ' ist laut extended ASCII table 255, bzw. FF. Das ist dann das was in x und y steht wenn nichts eingegeben wurde.

Laut Doku:
return: "the first byte of incoming serial data available (or -1 if no data is available)"

Und -1 im Zweier-Komplement ist FF...

So funktioniert es:
Code: [Select]

if(x != -1 && y != -1)
{
 Serial.print("x: ");
 Serial.print(x);
 Serial.print(" - y: ");
 Serial.println(y);
}


Fragt ab ob wirklich 2 Werte eingelesen wurden und schreibst sie nur dann raus.

Oder besser gleich serial.available() > 0:
http://arduino.cc/en/Serial/Available

Das kannst du bei dir auch um das Einlesen UND die if-Abfragen (bis auf die Clock Ausgabe) herumbauen. Dann macht er das nur wenn wirklich was eingelesen wurde:

Code: [Select]

if(Serial.available() > 1)
{
 x = Serial.read();
 y = Serial.read();
 
 
 if (x == '1' && y == '0')
 {
   .
   .
   .

}   //end Serial.available

if (clkEin)
{
   digitalWrite(CLK, HIGH); // CLK einschalten
   delay(5);
   digitalWrite(CLK, LOW); // CLK ausschalten
   delay(5);  
}


So wie es jetzt ist macht er den Vergleich auch in jeder Iteration von Loop, und da x und y dann -1 sind, passiert da halt nichts.

sonyfuchs

Code: [Select]
/*
  Der Motor ist bei 00 ausgeschaltet,
  mit 01 und 10 ändert man die Drehrichtung.
*/

int CLK =13; //Schrittgeschwindigkeit
int DIR =12; //Drehrichtung
int NEN = 9; //NOT ENABLE
int HAL = 8; //1/2
int QUA = 7; //1/4

char x;
char y;

void setup()
{
  pinMode(CLK, OUTPUT);
  pinMode(DIR, OUTPUT);
  pinMode(NEN, OUTPUT);
  pinMode(HAL, OUTPUT);
  pinMode(QUA, OUTPUT);
  Serial.begin(9600);
  Serial.println("Test");
  digitalWrite(NEN, HIGH);
}

void loop()
{
  static boolean clkEin=false;
  x = Serial.read();
  y = Serial.read();
 
  //if (Serial.available() > 0)
  //{
    Serial.print("x: ");
    Serial.print(x);
    Serial.print(" - y: ");
    Serial.println(y);
    Serial.println(digitalRead(NEN));
  //}
 
  if (x == '1' && y == '0')
  {
    digitalWrite(DIR, LOW);
    digitalWrite(NEN, LOW);
    clkEin=true;
  }
  else if (x == '0' && y == '0')
  {
    digitalWrite(NEN, HIGH);
    clkEin=false;
  }
  else if (x == '1' && y == '1')
  {
    digitalWrite(DIR, HIGH);
    digitalWrite(NEN, LOW);
    clkEin=true;
  }
  if (clkEin)
  {
    digitalWrite(CLK, HIGH); // CLK einschalten
    delay(5);
    digitalWrite(CLK, LOW); // CLK ausschalten
    delay(5); 
  }
}

wenn man die rauskomentierten 3 Zeilen wieder reinnimmt, läuft das Programm nicht mehr. Dabei ist das doch nur ne Ausgabe.

Go Up