Code
I wrote some code (using Bresenham's algorithm) for the turret:
/*
Simple 3 Axis Motion Controller
===============================
Written by Alan Cooper (agp.cooper@gmail.com)
This work is licensed under the Creative Commons Attribution-NonCommercial 2.5 License.
This means you are free to copy and share the code (but not to sell it).
*/
// Motor controller (CNC board) pin mapping:
#define DirX 2
#define DirY 3
#define DirZ 4
#define StepX 5
#define StepY 6
#define StepZ 7
#define Enable 8 // Active low
#define Laser 12 // Turn laser on or off
// Motor Controller direction settings
bool xReverse=false;
bool yReverse=true;
bool zReverse=false;
// Movement control parameters
bool pause=false; // Pause movement
long rampSteps; // start and stop ramp steps
long stepCountDown; // Movement steps remaining
long stepCountUp; // Movement steps completed
long movementRate; // Steps per second
long movementInterval; // Step period (uS)
long ramp; // Ramp up/down period
long xNew,yNew,zNew; // New co-ordinates
long xCurrent,yCurrent,zCurrent; // Current co-ordinates
void motionControl(void)
{
static unsigned long movementPreviousMicros=0;
static unsigned long movementCurrentMicros=0;
static long stepX,stepY,stepZ;
static long dx,dy,dz,ax,ay,az,sx,sy,sz,mx,my,mz;
// Process motion at end of current movement
if (stepCountDown==-1) {
stepCountDown=0;
stepCountUp=0;
// Determine movement parameters
dx=xNew-xCurrent;
dy=yNew-yCurrent;
dz=zNew-zCurrent;
ax=abs(dx);
ay=abs(dy);
az=abs(dz);
sx=xNew<xCurrent?-1:xNew>xCurrent?1:0;
sy=yNew<yCurrent?-1:yNew>yCurrent?1:0;
sz=zNew<zCurrent?-1:zNew>zCurrent?1:0;
if ((ax>=ay)&&(ax>=az)) {
mx=0;
my=ay-(ax>>1);
mz=az-(ax>>1);
stepCountDown=ax;
} else if ((ay>=ax)&&(ay>=az)) {
mx=ax-(ay>>1);
my=0;
mz=az-(ay>>1);
stepCountDown=ay;
} else {
mx=ax-(az>>1);
my=ay-(az>>1);
mz=0;
stepCountDown=az;
}
// Set counters
if (stepCountDown>0) {
stepCountUp=1;
} else {
stepCountDown=-1;
stepCountUp=-1;
}
// Set the stepper directions
if (xReverse) {
digitalWrite(DirX,(1-sx)>>1);
} else {
digitalWrite(DirX,(sx+1)>>1);
}
if (yReverse) {
digitalWrite(DirY,(1-sy)>>1);
} else {
digitalWrite(DirY,(sy+1)>>1);
}
if (zReverse) {
digitalWrite(DirZ,(1-sz)>>1);
} else {
digitalWrite(DirZ,(sz+1)>>1);
}
}
// Advance Steppers
if ((pause==false)&&(stepCountDown>0)) {
movementCurrentMicros=micros();
if (movementCurrentMicros-movementPreviousMicros>ramp) {
movementPreviousMicros=movementCurrentMicros;
// Start/stop ramp
if (stepCountUp<=rampSteps) ramp-=movementInterval;
if (stepCountDown<=rampSteps) ramp+=movementInterval;
// Advance steppers
stepX=0;
stepY=0;
stepZ=0;
if ((ax>=ay)&&(ax>=az)) {
if (my>=0) {
my-=ax;
stepY=sy;
}
if (mz>=0) {
mz-=ax;
stepZ=sz;
}
my+=ay;
mz+=az;
stepX=sx;
} else if ((ay>=ax)&&(ay>=az)) {
if (mx>=0) {
mx-=ay;
stepX=sx;
}
if (mz>=0) {
mz-=ay;
stepZ=sz;
}
mx+=ax;
mz+=az;
stepY=sy;
} else {
if (mx>=0) {
mx-=az;
stepX=sx;
}
if (my>=0) {
my-=az;
stepY=sy;
}
mx+=ax;
my+=ay;
stepZ=sz;
}
xCurrent+=stepX;
yCurrent+=stepY;
zCurrent+=stepZ;
// Step pulse (high for at least 10 us)
digitalWrite(StepX,abs(stepX));
digitalWrite(StepY,abs(stepY));
digitalWrite(StepZ,abs(stepZ));
delayMicroseconds(10);
digitalWrite(StepX,LOW);
digitalWrite(StepY,LOW);
digitalWrite(StepZ,LOW);
stepCountDown--;
stepCountUp++;
// Flag end of movement
if (stepCountDown==0) {
stepCountDown=-1;
stepCountUp=-1;
}
}
} else {
movementPreviousMicros=micros(); // Reset movement rate clock to ensure near full cycle on start
}
}
void setup()
{
// Initialise Motor Controller
// Note: The motor controller is using 1/16 micro-stepping
pinMode(DirX,OUTPUT);
pinMode(StepX,OUTPUT);
pinMode(DirY,OUTPUT);
pinMode(StepY,OUTPUT);
pinMode(DirZ,OUTPUT);
pinMode(StepZ,OUTPUT);
pinMode(Enable,OUTPUT);
pinMode(Laser,OUTPUT);
digitalWrite(DirX,LOW);
digitalWrite(StepX,LOW);
digitalWrite(DirY,LOW);
digitalWrite(StepY,LOW);
digitalWrite(DirZ,LOW);
digitalWrite(StepZ,LOW);
digitalWrite(Enable,LOW); // Active low
digitalWrite(Laser,LOW); // Laser off
// Reset Global Variables
pause=false;
xNew=0;
yNew=0;
zNew=0;
xCurrent=0;
yCurrent=0;
zCurrent=0;
stepCountDown=-1;
stepCountUp=-1;
movementRate=3200; // Step per second
movementInterval=1000000/movementRate; // Micro-seconds
rampSteps=6; // Number for steps for ramp up/down
// Precalculate ramp constant
ramp=movementInterval*(rampSteps+1);
// LED
pinMode(LED_BUILTIN,OUTPUT);
digitalWrite(LED_BUILTIN,LOW);
}
void loop()
{
// Used to keep track of motion
static int state=0;
// LED timer
static unsigned long intervalMillisLED=0;
static unsigned long currentMillisLED=0;
static unsigned long previousMillisLED=0;
// Use LED to indicate working
if (stepCountDown==0) {
intervalMillisLED=1000;
} else {
intervalMillisLED=100;
}
currentMillisLED=millis();
if (currentMillisLED-previousMillisLED>intervalMillisLED) {
previousMillisLED=currentMillisLED;
digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
}
/* Motion Command */
// Line scanner (-100 to 100 x -100 to 100 )
if (stepCountDown==-1) {
if ((state&2)==0) {
// Laser off
digitalWrite(Laser,LOW);
xNew=-100;
yNew=-100+(state>>1);
zNew=0;
} else {
// Laser on
digitalWrite(Laser,HIGH);
xNew=+100;
yNew=-100+(state>>1);
zNew=0;
}
state++;
if (state>401) state=0;
}
/* Process Motion Command */
motionControl();
}
The code scans left to right, top to bottom, 201 lines high by 201 pixels wide.
I set the stepper drive up for 0.5A per phase (because the 12v wall plug power supply is a bit under rated for this application) and 1/16 micro stepping.
The CNC board has a design fault in that the micro-steeping shunts are to ground rather than to Vcc, so it is alway set to full step regardless of the shunt setting. I had to jump the shunts pins to Vcc.
The code still needs fine tuning to peak the maximum step rate. It is currently running fine at 3200 PPS (60 RPM).
Assembled and Operating
Here is the turret assembled and operating:
And here is part of the trace on the wall about 5m away:
It is only about 50% of the trace due to the camera shutter speed.
Notice the wiggle! It is about a one micro step (i.e. 1/16 of a full step) high and is quite consistent, probably vibration. If you watch each scan line, some scan lines appear to not move at 1/16 micro stepping. Suggesting the turret accuracy is about 1/8 of a step or about 0.1 degrees. For the next iteration I will drop back to 1/8 micro stepping.
Magic
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.