Schrittmotoransteuerung mit GRBL und Signalumsetzer

Hi Community,

bin gerade dabei einen kleinen Plotter zum bemalen von Leiterplatten zu basteln. Die Mechanik habe ich inzwischen recht gut im Griff. Bei der Elektronik sieht es da etwas anderst aus.
Der Schrittmotor scheint immer mal wieder Schritte zu überspringen oder nicht ganz zurecht zukommen.
Die Elektronik ist nach folgendem Schema aufgebaut.

Auf dem Arduino Uno läuft die freie Software GRBL 1.1. Diese gibt die Signale Step, Direction und Enable für jede Achse über ingesamt 9 Pins aus. Diese gebe ich an den Siganlumsetzer weiter. Das ist ein selbst programmierter Atmel ATtiny48-PU. Dieser gibt für jede Achse jeweils die vier Signale zum ansprechen der Motorwindungen aus, welche über einen Schrittmotortreiber an den eigentlichen Schrittmotor 28BYJ-48 weitergegeben werden.
Nachdem ich mich in die Konfiguration von GRBL eingelesen habe und auch schon einiges ausprobiert habe, funktioniert es leider noch nicht so wie es soll. Der Motor kommt immer wieder ins „humpeln“. Meine Vemrutung ist aufgrund der Geräusche des Schrittmotors, dass Schritte übersprungen werden oder mein Eigenbau-Signalumsetzer nicht mit der Frequenz mitkommt. Ein Herabsetzen der Signalgeschwingdigkeit in GRBL hat allerdings auch nicht geholfen.
Man muss dazu sagen, dass es das erste mal ist, dass ich etwas außerhalb der Theorie mit Schrittmotoren zu tun habe.

Den Quellcode von dem Signalumsetzer habe ich unten hinzugefügt. Hoffe ihr könnt mir damit helfen, den Schrittmotor korrekt anzusteuern.
Ich will jetzt hier nicht alles mit Informationen überladen. Solltet ihr zur Beurteilung noch Infos benötigen, schreibt dies gerne. Sofern möglich werde ich diese dann nachliefern.
Der Schrittmotor und die Treiber sollten meiner Einschätzung nicht das Problem sein. Diese wurden als Bundle verkauft.

Im Internet habe ich auch schon Schrittmotor-Signalumsetzer mit integriertem Treiber gesucht, leider vergebens. Vielleicht habt ihr da auch einen Tipp, welche IC´s man da nutzen könnte.

Hoffe Ihr könnt mir helfen das Ding endlich ans laufen zu bekommen.

Viele Grüße
kleinerkaktus

P.S.: Der einfachheit halber, habe ich den Quellcode nur für eine Achse hier eingestellt. Der Qeuellcode ist im richtigen Programm einfach für jede Achse innerhalb der Loop kopiert und die Bezeichnungen der Variablen angepasst worden.

Main.cpp:

/*
* PE.cpp
*/

#include <avr/io.h>
#include <util/delay.h>
#include "ULN2003_Driver.h"


// +++++++++++++++++++++++++++++++++++++++++++++++++++ DEFINITIONS +++++++++++++++++++++++++++++++++++++++++++++++++++

//Frequency CPU
# define F_CPU 16.000;

int main(void)
{
	// +++++++++++++++++++++++++++++++++++++++++++++++++++ SETUP +++++++++++++++++++++++++++++++++++++++++++++++++++
	
	initPortsDataDirection();


	// +++++++++++++++++++++++++++++++++++++++++++ Variablendeklaration +++++++++++++++++++++++++++++++++++++++++++

	//Allgemeine Variable
	uint8_t iCountMax = 8;

	//X-Achse

	bool bXFlankenmerker = false;
	bool bXDI_Signal = false;
	bool bXDI_Direction = false;
	uint8_t iCountX = 1;


	// +++++++++++++++++++++++++++++++++++++++++++++++++++ LOOP +++++++++++++++++++++++++++++++++++++++++++++++++++
	while (1)
	{
		// ++++++++++++++++++++++++++++++++++++++++++++ X-Achse +++++++++++++++++++++++++++++++++++++++++++++++++++++++

		// >>>>>>>>>>>>> X-Step einlesen (Flanke X) <<<<<<<<<<<<<

		bXDI_Signal = (PINB & (1<<PINB5));
		bXDI_Direction = (PINB & (1<<PINB4));

		if ((bXDI_Signal == true) && (bXFlankenmerker == false))
		{
			if (bXDI_Direction == true)
			{
				iCountX++;
			}
			else
			{
				iCountX--;
			}
			bXFlankenmerker = true;
			//_delay_ms(5);
		}
		else if (bXDI_Signal == false)
		{
			bXFlankenmerker = false;
		}

		// >>>>>>>>>>>>> Grenzen Zähler festlegen <<<<<<<<<<<<<

		if (iCountX > iCountMax)
		{
			iCountX = 1;
		}
		else if (iCountX <=0)
		{
			iCountX = 8;
		}

		// >>>>>>>>>>>>> Zustände <<<<<<<<<<<<<
		switch (iCountX)
		{
			case 1: XI1_On(); XI2_Off(); XI3_Off(); XI4_Off(); break; //1000
			case 2: XI1_On(); XI2_On(); XI3_Off(); XI4_Off(); break; //1100
			case 3: XI1_Off(); XI2_On(); XI3_Off(); XI4_Off(); break; //0100
			case 4: XI1_Off(); XI2_On(); XI3_On(); XI4_Off(); break; //0110
			case 5: XI1_Off(); XI2_Off(); XI3_On(); XI4_Off(); break; //0010
			case 6: XI1_Off(); XI2_Off(); XI3_On(); XI4_On(); break; //0011
			case 7: XI1_Off(); XI2_Off(); XI3_Off(); XI4_On(); break; //0001
			case 8: XI1_On(); XI2_Off(); XI3_Off(); XI4_On(); break; //1001
			default: XI1_Off(); XI2_Off(); XI3_Off(); XI4_Off(); break; //0000
		}
	}
}

Driver:

/*
* ULN2003_Driver.h
*/


#ifndef ULN2003_DRIVER_H_
#define ULN2003_DRIVER_H_


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ DataDirectionsRegister setzen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void initPortsDataDirection()
{
	////>>>>>>>>>>>>>>>>> Ein-/ Ausgänge definieren --> Data Direction Register (1 - Ausgang, 0-Eingang) <<<<<<<<<<<<<<<<<

	//>>>>>>>>>>>>>>>>> X-Achse <<<<<<<<<<<<<<<<<
	// PortB: OUTPUTS
	DDRB |= (1<<DDB0);
	DDRB |= (1<<DDB1);
	DDRB |= (1<<DDB2);
	DDRB |= (1<<DDB3);

	//PortB: INPUTS
	DDRB &= ~(1<<DDB4);
	DDRB &= ~(1<<DDB5);
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Funktionen der Ausgänge ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//X-Achse

void XI1_On(){PORTB |= (1<<PORTB0);}
void XI1_Off(){PORTB &= ~(1<<PORTB0);}

void XI2_On(){PORTB |= (1<<PORTB1);}
void XI2_Off(){PORTB &= ~(1<<PORTB1);}

void XI3_On(){PORTB |= (1<<PORTB2);}
void XI3_Off(){PORTB &= ~(1<<PORTB2);}

void XI4_On(){PORTB |= (1<<PORTB3);}
void XI4_Off(){PORTB &= ~(1<<PORTB3);}


#endif /* ULN2003_DRIVER_H_ */

Hallo!

Ich übersetze mal:

  • Wenn Pin PORTB0 == 1 ist, dann setze das zweite Bit von PORTB, also PORTB1 auf 1.
  • Wenn Pin PORTB0 == 0 ist, dann lösche das erste Bit von PORTB, also PORTB0 auf 0. (Das ist es doch schon…)

Also: PORTB0 ist direkt der Pin B0, und du kannst den mit PORTB0=0oder PORTB0=1 ändern. Das gilt auch für die DDR-Einstellungen.


Noch eine konzeptionelle Sache:

Eigentlich müsstest du eine Datei ULN2003_Driver.c(pp) haben, die die eigentlichen Funktionen enthält. In die ULN2003_Driver.h gehören nur die Deklarationen (und das #ifndef / #define), sie sollte so aussehen:

/*
* ULN2003_Driver.h
*/


#ifndef ULN2003_DRIVER_H_
#define ULN2003_DRIVER_H_

void initPortsDataDirection(){};

void XI1_On(){};
void XI1_Off(){};

void XI2_On(){};
void XI2_Off(){};

void XI3_On(){};
void XI3_Off(){};

void XI4_On(){};
void XI4_Off(){};


#endif /* ULN2003_DRIVER_H_ */

Es geht dabei darum, der PE.cpp klar zu machen, daß diese Funktionen zwar irgendwo existieren, aber eben nicht, deren Code mit in die PE.cpp zu kopieren (Nichts anderes macht das #include)
Das hat zwei Gründe:

  1. Wenn du Änderungen an der PE.cpp machst und das Programm neu kompilierst, dann muß nur die PE.cpp neu kompiliert werden. Der Code in der ULN2003_Driver.cpp wurde ja nicht geändert, und muß nicht neu kompiliert werden, das spart Zeit. Naja, bei Microcontroller-Projekten nicht wirklich.
  2. Bei größeren Projekten mit vielen Dateien sollen deine Funktionen evtl. aus mehreren der Dateien aufgerufen werden. Aber man darf sie nur an einer Stelle im Code stehen haben. Mit der Header-Datei teilt mal all den Dateien mit, daß es die Funktionen irgendwo gibt.
1 Like

Hallo,
falls der Code fehlerfrei ist würde ich vermuten dass die Spannung für den Motor zu niedrig ist.
Der ist ja mit 12V angegeben. Bei 5V ist die Stromanstiegsgeschwindigkeit eventuell nicht groß genug und er entwickelt nicht genügend Drehmoment.
Wie verhält er sich denn ohne Last?
Edit: ich sehe gerade, den gibt es als 5V und 12V Version.

Hallo ihr beiden,

danke für die Unterstützung.
Ich werde mir das mal eure Tipps nach diesem Wochenende genauer ansehen. Da sollte sich dann wieder Zeit finden.

Besten Dank euch.
Ich melde mich wieder und dann hoffentlich mit der Lösung und evtl. dem gesamten Code hier öffentlich. Das sollte dann evtl. auch anderen Leuten helfen können.

Viele Grüße
kleinerkaktus

Hi community,

jetzt melde ich mich nochmal zu Wort.
Habe wieder weitergebastelt.

@sweber
Verstehe leider nicht so ganz was du mit dem ersten Teil deines Beitrags gemeint hast. Das mit dem Header verstehe ich.

Inzwischen habe ich auch die Vermutung, dass evtl. der Motor am Treiber falsch angeschlossen sein könnte. Habe das ganze jetzt mal auf Fullstep umprogrammiert.

case 1: XI1_On(); XI2_Off(); XI3_Off(); XI4_Off(); break; //1000
case 2: XI1_Off(); XI2_On(); XI3_Off(); XI4_Off(); break; //0100
case 3: XI1_Off(); XI2_Off(); XI3_On(); XI4_Off(); break; //0010
case 4: XI1_Off(); XI2_Off(); XI3_Off(); XI4_On(); break; //0001
default: XI1_Off(); XI2_Off(); XI3_Off(); XI4_Off(); break; //0000

Und den Motor folgendermaßen angeschlossen:
grafik
Motor - Treiber

  1. Blue - IN1
  2. Pink - IN2
  3. Yellow - IN3
  4. Orange - IN4

Die Eingänge sind also 1 zu 1 durchverbunden. Ist das denn so richtig?
Hoffe Ihr könnt mir damit weiterhelfen :slight_smile:

Viele Grüße
kleinerkaktus

Naja, das ist aber wichtig.

Du setzt deine Ausgänge auf irgendwelche wilden Werte , und nicht so, wie das sein müsste. Kein Wunder Daß der Motor nicht tut, was er soll. Er wird nicht richtig angesteuert…

Hi sweber,

würde es gerne verstehen. Kannst du mir das erklären?

Oder meinst du die Bitoperationen?

Viele Grüße
kleinerkaktus