Laser Cutter Electronics
File:Laser-control board.pdf
File:Laser-motor driver.pdf
Code
/*
* Laser Engraver Controller - ver. 1.0
*
* Written by: Andrew Kilpatrick
* Copyright: 2009
*
* Runs on PIC16F877A
*
* Functional Description:
* - support for connection to emc2 or similar host-driven CNC software
* - parallel port or other type of bit-bang connection to PC
* - E-stop and system run control support (amp enable, charge pump, etc.)
* - controls 3 stepper motors (X, Y, Z)
* - controls laser PWM control
*
* Hardware I/O:
* - RA0 - laser power bit 0 - input - active high
* - RA1 - laser power bit 1 - input - active high
* - RA2 - laser power bit 2 - input - active high
* - RA3 - laser power bit 3 - input - active high
* - RA4 - laser defeat - input - active high
*
* - RB0 - E stop loop - input - 0 = run, 1 = stop
* - RB1 - Z dir - input - 0 = back, 1 = forward
* - RB2 - Y dir - input - 0 = back, 1 = forward
* - RB3 - X dir - input - 0 = back, 1 = forward
* - RB4 - laser activate - input - 0 = off, 1 = on
* - RB5 - Z step - input - rising edge - move one step
* - RB6 - Y step - input - rising edge - move one step
* - RB7 - X step - input - rising edge - move one step
*
* - RC0 - amp enable LED - output - 0 = off, 1 = on
* - RC1 - laser LED - output - 0 = off, 1 = on
* - RC2 - laser PWM - output - 0 = off, 1 = on
* - RC4 - Z motor phase A - output
* - RC5 - Z motor phase B - output
* - RC6 - Z motor phase C - output
* - RC7 - Z motor phase D - output
*
* - RD0 - X motor phase A - output
* - RD1 - X motor phase B - output
* - RD2 - X motor phase C - output
* - RD3 - X motor phase D - output
* - RD4 - Y motor phase A - output
* - RD5 - Y motor phase B - output
* - RD6 - Y motor phase C - output
* - RD7 - Y motor phase D - output
*
* - RE0 - E stop output - output - 0 = stop, 1 = run
* - RE1 - amp enable input - input - 0 = disable, 1 = enable
* - RE2 - charge pump input - input - high pulses to charge
*
*/
#include <system.h>
#pragma CLOCK_FREQ 20000000
#pragma DATA 0x2007, _CP_OFF & _DEBUG_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _HS_OSC & _WDT_ON & _PWRTE_ON
// inputs
#define LASER_POWER_1_IN porta.0
#define LASER_POWER_2_IN porta.1
#define LASER_POWER_4_IN porta.2
#define LASER_POWER_8_IN porta.3
#define LASER_DEFEAT_IN porta.4
#define E_STOP_IN portb.0
#define X_DIR_IN portb.3
#define Y_DIR_IN portb.2
#define Z_DIR_IN portb.1
#define X_STEP_IN portb.7
#define Y_STEP_IN portb.6
#define Z_STEP_IN portb.5
#define LASER_IN portb.4
#define AMP_ENABLE_IN porte.1
#define CHARGE_PUMP_IN porte.2
// outputs
#define AMP_ENABLE_LED portc.0
#define LASER_ON_LED portc.1
#define E_STOP_OUT porte.0
// laser setting
#define LASER_TICKLE 0x01
// motor step size - 1 = half-step, 2 = full step
#define X_STEP_SIZE 1
#define Y_STEP_SIZE 1
#define Z_STEP_SIZE 1
// run state machine
#define STATE_IDLE 1
#define STATE_RUN 2
#define STATE_E_STOP 3
unsigned char run_state;
// globals
unsigned char x_phase; // the X motor step phase
unsigned char y_phase; // the Y motor step phase
unsigned char z_phase; // the Z motor step phase
unsigned char run; // 1 = normal, 0 = stopped
unsigned char charge_pump; // 0 = stopped, >0 = run
unsigned char old_ch_pump;
unsigned char laser_init; // 1 = laser initialized, 0 = not initialized
unsigned char laser_power; // laser power level
unsigned char laser_on; // 1 = laser on, 0 = laser off
unsigned char flash; // temp flasher variable
unsigned char x_step; // temp x step state
unsigned char y_step; // temp y step state
unsigned char z_step; // temp z step state
// function prototypes
void step_laser(void);
// main loop
void main() {
// set up IO
porta = 0x00;
trisa = 0xff; // inputs
adcon1 = 0x06; // all digital pins
portb = 0x00;
trisb = 0xff; // inputs
option_reg.NOT_RBPU = 0; // weak pullups
portc = 0x00;
trisc = 0x00; // outputs
AMP_ENABLE_LED = 0;
LASER_ON_LED = 0;
ccpr1l = 0x00;
ccp1con = 0x0c; // PWM mode
t2con = 0x05; // prescaler 4, timer on
portd = 0x00;
trisd = 0x00; // outputs
trise.PSPMODE = 0; // no data port
porte = 0x00;
trise &= 0xfe; // RE0 = output, RE1-2 = input
E_STOP_OUT = 1; // no E stop
t1con = 0x21; // 1:4 prescaler, internal clock, timer on
// reset temp vars
x_phase = 0;
y_phase = 0;
z_phase = 0;
run = 0;
laser_init = 0;
laser_on = 0;
flash = 0;
x_step = 0;
y_step = 0;
z_step = 0;
run_state = STATE_IDLE;
charge_pump = 0;
old_ch_pump = 0;
// bootup light dance
AMP_ENABLE_LED = 0;
LASER_ON_LED = 1;
delay_ms(250);
delay_ms(250);
AMP_ENABLE_LED = 1;
LASER_ON_LED = 0;
delay_ms(250);
delay_ms(250);
AMP_ENABLE_LED = 0;
// loop
while(1) {
clear_wdt();
// do stuff every 50ms
if(pir1.TMR1IF) {
pir1.TMR1IF = 0;
// charge pump
if(charge_pump) charge_pump --;
// EMERGENCY STOP!
if(E_STOP_IN) {
run_state = STATE_E_STOP;
E_STOP_OUT = 0;
// turn off the laser
ccpr1l = 0x00; // power level to 0
ccp1con &= 0xcf; // power level to 0
laser_power = 0;
laser_on = 0;
LASER_ON_LED = 0;
// turn off motors
portd = 0;
portc &= 0x0f;
// LED flashing
flash = (flash + 1) & 0x07;
if(flash & 0x04) {
AMP_ENABLE_LED = 1;
}
else {
AMP_ENABLE_LED = 0;
}
}
// E STOP cleared - enter idle state
if(run_state == STATE_E_STOP && !E_STOP_IN) {
run_state = STATE_IDLE;
AMP_ENABLE_LED = 0;
E_STOP_OUT = 1;
}
// amplifier enabled - enter run state
if(run_state == STATE_IDLE && AMP_ENABLE_IN && charge_pump > 0x7f) {
run_state = STATE_RUN;
AMP_ENABLE_LED = 1;
ccpr1l = LASER_TICKLE; // make tickle puses for the laser
ccp1con |= 0x30; // PWM mode
}
// amplifier disabled - enter idle state
if(run_state == STATE_RUN && (!AMP_ENABLE_IN || charge_pump < 0x80)) {
run_state = STATE_IDLE;
AMP_ENABLE_LED = 0;
// turn off the laser
ccpr1l = 0x00; // power level to 0
ccp1con &= 0xcf; // power level to 0
laser_power = 0;
laser_on = 0;
LASER_ON_LED = 0;
// turn off motors
portd = 0;
portc &= 0x0f;
}
// handle laser power adjustments in run state
if(run_state == STATE_RUN) {
// laser power adjust
if(LASER_DEFEAT_IN) {
// force laser into tickle
ccpr1l = LASER_TICKLE;
laser_power = 0;
LASER_ON_LED = 0;
}
else {
laser_power = (porta & 0x0f) << 4;
// if laser is on, change power immediately
if(laser_on) {
ccpr1l = laser_power;
LASER_ON_LED = 1;
}
}
}
}
// handle realtime run stuff
if(run_state == STATE_RUN) {
step_laser();
}
// handle charge pump on rising edge
if(CHARGE_PUMP_IN && !old_ch_pump) {
if(charge_pump != 0xff) charge_pump ++;
}
old_ch_pump = CHARGE_PUMP_IN;
}
}
void step_laser(void) {
// step inputs are low - reset the flags
if(!X_STEP_IN) {
x_step = 0;
}
if(!Y_STEP_IN) {
y_step = 0;
}
if(!Z_STEP_IN) {
z_step = 0;
}
// x step
if(!x_step && X_STEP_IN) {
x_step = 1;
// forward
if(X_DIR_IN) {
x_phase = (x_phase - X_STEP_SIZE) & 0x07;
}
// reverse
else {
x_phase = (x_phase + X_STEP_SIZE) & 0x07;
}
// phases
if(x_phase == 0) portd = (portd & 0xf0) | 0x05;
if(x_phase == 1) portd = (portd & 0xf0) | 0x0d;
if(x_phase == 2) portd = (portd & 0xf0) | 0x09;
if(x_phase == 3) portd = (portd & 0xf0) | 0x0b;
if(x_phase == 4) portd = (portd & 0xf0) | 0x0a;
if(x_phase == 5) portd = (portd & 0xf0) | 0x02;
if(x_phase == 6) portd = (portd & 0xf0) | 0x06;
if(x_phase == 7) portd = (portd & 0xf0) | 0x04;
}
// y step
if(!y_step && Y_STEP_IN) {
y_step = 1;
// forward
if(Y_DIR_IN) {
y_phase = (y_phase - Y_STEP_SIZE) & 0x07;
}
// reverse
else {
y_phase = (y_phase + Y_STEP_SIZE) & 0x07;
}
// phases
if(y_phase == 0) portd = (portd & 0x0f) | 0x50;
if(y_phase == 1) portd = (portd & 0x0f) | 0xd0;
if(y_phase == 2) portd = (portd & 0x0f) | 0x90;
if(y_phase == 3) portd = (portd & 0x0f) | 0xb0;
if(y_phase == 4) portd = (portd & 0x0f) | 0xa0;
if(y_phase == 5) portd = (portd & 0x0f) | 0x20;
if(y_phase == 6) portd = (portd & 0x0f) | 0x60;
if(y_phase == 7) portd = (portd & 0x0f) | 0x40;
}
// z step
if(!z_step && Z_STEP_IN) {
z_step = 1;
// forward
if(Z_DIR_IN) {
z_phase = (z_phase - Z_STEP_SIZE) & 0x07;
}
// reverse
else {
z_phase = (z_phase + Z_STEP_SIZE) & 0x07;
}
// phases
if(z_phase == 0) portc = (portc & 0x0f) | 0x50;
if(z_phase == 1) portc = (portc & 0x0f) | 0xd0;
if(z_phase == 2) portc = (portc & 0x0f) | 0x90;
if(z_phase == 3) portc = (portc & 0x0f) | 0xb0;
if(z_phase == 4) portc = (portc & 0x0f) | 0xa0;
if(z_phase == 5) portc = (portc & 0x0f) | 0x20;
if(z_phase == 6) portc = (portc & 0x0f) | 0x60;
if(z_phase == 7) portc = (portc & 0x0f) | 0x40;
}
// laser
if(LASER_IN) {
ccpr1l = laser_power;
LASER_ON_LED = 1;
laser_on = 1;
}
else {
ccpr1l = LASER_TICKLE;
LASER_ON_LED = 0;
laser_on = 0;
}
}