Re: Arduino based Laser Control
Posted: Sun Sep 28, 2014 7:54 pm
char bro I don't mind waiting at all it will be a few weeks before all the parts show up
A place for Gear Heads to gather
https://gearoticmotion.com/GearForum/
Code: Select all
#include <Wire.h>
#include <LiquidCrystal.h>
#include <PWM.h>
//Setup for Diags..
#define DIAGS 0
#define CNCMODE 1
#define NGRAVE 2
//frequency and pins used.
int32_t basefrequency = 5000; //5khz base freq
int8_t PulsePin = 9; //Pin 9 will control the laser.
int8_t BoardLED = 13;
int8_t FireLED = 12;
int8_t PowerLED = 5;
int8_t Pot = 4;
int8_t Buttons = 8; //analogue and digital are same numbers..be carefull if you use other pins..
int8_t Buttons1 = 10;
int8_t CurButton = 0; //currently pressed button
int8_t CountMode = 0; //the mode of the up/down counter..
bool WaitButtonZero = false;
int8_t OneMicro = 16;
int8_t OpMode = 0;
volatile unsigned short SerialIn = 0;
volatile unsigned short LastSerial = 0;
//volatile unsigned long SpinOnCnt = 0;
//volatile unsigned long SpinOffCnt = 0;
//volatile bool SpindleOn = false; //remote spindle command..
//volatile bool SpindlePWM = false; //remote spindle command current state..
//vars for control
bool PowerOn = false;
bool Tickle = false; // On Off Safety watchdog .. monitor the tickle for laser health
volatile int16_t Duty = OneMicro; //this is the pulse to pulse firepower during a shot.
volatile int32_t cnt = 0; //this is a global interrupt counter..
volatile bool FIRE = false;
//initialize the lcd..its on a0-a6 lines in my leonardo..
//LiquidCrystal lcd( 23,22,21,20,19,18); // set the LCD address to 0x27 for a 20 chars and 4 line display
// the main control variable for diags mode..
int16_t Power = 50; //0 - 100%
unsigned long PWMPower = 0;//0- 100% for the incoming PWM..
int16_t Freq = 5000; // frequency of pulses..
int8_t PotMode = 0;
volatile int32_t FireCount = 0; //number of pulses to fire in diags
volatile int16_t ReLoad = 1;
void setup()
{
// lcd.begin(20,4); // initialize the lcd
// lcd.setCursor(3,0);
// lcd.print("Gearotic Laser");
// lcd.setCursor(0,2);
// lcd.print("Waiting for Unix.");
InitTimers(); //start up the timers.
bool timeron = SetPinFrequency( PulsePin, basefrequency ); //set pulsepin to 5Khz
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt, we'll use this one for fastpwm
attachInterrupt( 0, DUMMY, RISING ); //pulse external monitor
attachInterrupt( 4, DUMMY, CHANGE ); //sets the routine to call..
pwmWrite( PulsePin, 8); //set for 1us for laser tickle
if( timeron ) //if the timercall worked..
{
pinMode( BoardLED, INPUT); // board led life
pinMode( FireLED, OUTPUT); // fire button led
pinMode( PowerLED, OUTPUT); //power control led & control
pinMode( 2, INPUT); //inputs from breakout board
digitalWrite( 2, LOW );
pinMode( 3, INPUT); //inputs from breakout board
digitalWrite( 3, LOW );
pinMode( 11, INPUT); //inputs from breakout board
digitalWrite( 11, HIGH );
digitalWrite( PowerLED, PowerOn); //turn off led for power at start
pinMode( Pot, INPUT); //pot for variable control
digitalWrite(4, HIGH); //set for a pullup resistor
pinMode( Buttons, INPUT); //Jog buttons as analogue input
digitalWrite(Buttons, HIGH);
pinMode( Buttons1, INPUT); //Rest of buttons
digitalWrite(Buttons1, HIGH);
}
//SetDisplay( true ); //and draw the display mask
}
//main idle loop..
void loop()
{
HeartBeat(); //flash the board led if fire isnt flashing..
FlashFire(); // flash the fire button if we are going to fire..
WatchDog(); // ensure all is safe.
DiagsOut(); //display test text if demanded..not used
PotControl(); //use the pot for values..
GetButton();
if( OpMode == DIAGS ) DiagFire();
//if( OpMode == NGRAVE) NGraveFire();
LaserOnOff();
DoButtonCount(); // make freq or firecount set by button if needed.
//set power and other vars, then display..
//SetDisplay( false );
}
void DoButtonCount()
{
int8_t Accel = 1; //this is the accel of button freq, adjust as needed.
static int16_t Adder = Accel;
int16_t Freqmax = 200;
static int32_t Bres = Freqmax;
if( CurButton == 0)
{
Adder = Accel;
Bres = Freqmax;
return;
}
int16_t oldfire = FireCount;
Bres += Adder;
while( Bres > Freqmax ) //not very ellegant, fix later. just a timer for easier button press
{
if( CurButton == 4 ) //left arrow, switch pot to count mode..
CountMode--;
if( CurButton == 6 ) //left arrow, switch pot to dutycyle..
CountMode++;
if( CountMode < 0) CountMode = 2;
if( CountMode > 2) CountMode = 0;
Bres -= Freqmax;
if( CountMode == 0 && CurButton == 5) Freq++;
if( CountMode == 2 && CurButton == 5) FireCount++;
if( CountMode == 0 && CurButton == 7) Freq--;
if( CountMode == 2 && CurButton == 7) FireCount--;
if( FireCount < 0 ) FireCount = 5000;
if( FireCount > 5000 ) FireCount = 0;
if( Freq <= 0 ) Freq = 1;
if( Freq > 5000 ) Freq = 5000;
int oldop = OpMode;
if( CountMode == 1 && CurButton == 5 ) OpMode++;
if( CountMode == 1 && CurButton == 7 ) OpMode--;
if( OpMode > 2 ) OpMode = 0; //these two lines just make the opMode toggle up or down..
if( OpMode < 0 ) OpMode = 2;
//reopen file if required.
if( OpMode == NGRAVE && oldop != NGRAVE)
{
//detach the spindle interrupts.
detachInterrupt(4);
attachInterrupt( 1, DUMMY, RISING ); //reattach the spindle interrupt
}
if( OpMode != NGRAVE && oldop == NGRAVE )
{
attachInterrupt( 4, DUMMY, CHANGE ); //reattach the spindle interrupt
detachInterrupt(1);
}
}
if( oldfire != FireCount ) ReLoad = FireCount;
Adder += Accel;
//faster..damnit..faster. :)
if( Adder == 100 ) Adder *= 2;
if( Adder == 400) Adder *= 2;
if( Adder == 1000) Adder *= 2;
}
void DiagFire()
{
if( CurButton == 2 ) FireCount = ReLoad;
//FIRE!!!!
if( CurButton == 1 && FireCount > 0 ) FIRE = true;
if( CurButton == 0 && FIRE && OpMode == DIAGS ) FIRE = false; //turn it off if no button is pressed in Diags mode.
}
void LaserOnOff()
{
//this controls Laser ON/OFF
if( CurButton == 3 ) //power call , but only once per press
{
if( !WaitButtonZero) PowerOn = !PowerOn;
digitalWrite( PowerLED, PowerOn); //Set Power condition;
WaitButtonZero = true;
}
}
void DUMMY()
{
}
int16_t FireLaser()
{
int16_t ret = OneMicro; //usually we'll return 1 microsecond.
static int16_t Counter = basefrequency;
if( FIRE )
{
if( OpMode == DIAGS )
ret = Power << 5; //we'll return this power only if the counter says so..
else
ret = PWMPower << 5; //in CNC modes we return the duty cycle power
if( ret > 3191 ) ret = 3191; //set a maximum pulse wdith here for your cpu speed
//now decide on a frequcny basis is this is a shot time..
Counter += Freq;
if( Counter >= basefrequency )
{
Counter -= basefrequency;
//if( SpindleOn && FireCount > 2000 ) FireCount = 2000;
if( FireCount != 5000) FireCount--;
if( FireCount <= 0 ) FIRE = false;
}
else
ret = OneMicro;
}
if( ret < OneMicro ) ret = OneMicro; //no msaller than 1 microsecnd
return ret;
}
//display the text
///// Text Blockout
////////////////////////
// Pwr //
//Frq-hz 100 Pulses //
// 5000 <---> 5000 //
////////////////////////
//void SetDisplay( bool clear )
//{
// if( clear )
// {
// lcd.clear(); //x,y
// lcd.setCursor(8, 0);
// lcd.print("Pwr");
// lcd.setCursor(0, 1);
// lcd.print("Frq-hz");
// lcd.setCursor(14,1);
// lcd.print("Pulses");
// }
// static int8_t slw = 0;
// slw++;
// if( slw % 8 )
// if( SpindleOn && OpMode == CNCMODE ) //display of remote power setting..
// {
// lcd.setCursor(11, 0);
// lcd.print("-Rem:");
// lcd.setCursor(16, 0);
// lcd.print(" ");
// lcd.setCursor(16, 0);
// lcd.print(PWMPower,10);
// }
// else
// if( OpMode == NGRAVE )
// {
// lcd.setCursor(11, 0);
// lcd.print("-Rem:");
// lcd.setCursor(16, 0);
// lcd.print(" ");
// lcd.setCursor(16, 0);
// lcd.print(LastSerial,10);
// }
// else
// {
// lcd.setCursor(11, 0);
// lcd.print(" ");
// lcd.setCursor(16, 0);
// lcd.print(" ");
// }
//power
// lcd.setCursor(8,1);
// lcd.print( Power , 10 );
// lcd.print( "% " );
//frequency
// lcd.setCursor(3,3);
// lcd.print( Freq , 10 );
// lcd.print(" ");
//fire count
// lcd.setCursor(15,3);
// if( FireCount != 5000)
// {
// lcd.print( FireCount , 10 );
// lcd.print(" ");
// }
// else
// lcd.print(" CW ");
//and count mode
//lcd.setCursor(8,3);
// switch( CountMode )
// {
// case (0) :
// lcd.print("<<---");
// break;
// case (1) :
// lcd.print(" ^^^ ");
// break;
// case (2) :
// lcd.print("--->>");
// break;
// }
//static int toggle = 0;
// static bool twinkle = true;
// toggle++;
// if( toggle > 20 )
// {
// toggle = 0;
// twinkle = !twinkle;
// lcd.setCursor( 0,4 );
// if( twinkle ) lcd.print("*");
// else lcd.print("#");
// }
//erase the mopde line..
// lcd.setCursor( 0,2 );
// lcd.print(" ");
// if( SpindleOn && OpMode == DIAGS)
// OpMode = CNCMODE; //auto switch from Diags
// switch (OpMode)
// {
// case(0):
// lcd.setCursor( 8,2 );
// lcd.print("Diag");
// break;
// case(1):
// lcd.setCursor( 7,2 );
// lcd.print("CNC-CW");
// break;
// case(2):
// lcd.setCursor( 7,2 );
// lcd.print("NGRAVE");
// break;
//}
//}
//helper functions
void FlashFire()
{
static int8_t Flashtime = 0;
static bool fireledstate;
if( FireCount == 0 )
fireledstate = false;
if( Flashtime++ > 10 ) //this 10 should be change for a pleasant flash tempo..
{
digitalWrite( FireLED, fireledstate );
fireledstate = !fireledstate;
Flashtime = 0;
}
}
//flash the board led to show we're alive
void HeartBeat()
{
static int8_t Beat = 0;
static bool beatstate;
if( FireCount != 0 ) return;
if( Beat++ > 10 ) //this 10 should be change for a pleasant flash tempo..
{
//digitalWrite( BoardLED, beatstate );
beatstate = !beatstate;
Beat = 0;
}
}
//watchdog function, kill laser power if tickle stops..
//or other checks all of which can be put here..
void WatchDog()
{
static int lastcnt = cnt; //activity monitor
if( lastcnt != cnt ) Tickle = true;
else Tickle = false;
lastcnt = cnt;
//set other checks here... too long a shot maybe...
if( !Tickle )
{
PowerOn = false;
digitalWrite( PowerLED, PowerOn); //turn off led for power at start
}
}
//use of the pot for duty or frequency.
void PotControl()
{
int32_t val = analogRead( Pot + 2 ); //the analogue for the pot pin
if( PotMode == 0 ) // duty cycle.. only mode at present..
{
Power = val / 10.23;
if( Power < 1 ) Power = 1;
if( Power > 100) Power = 100;
Power = 101 - Power;
}
}
//diags text for testing and debugging..
void DiagsOut()
{
//just for testing pins..
return; //only use if we demand it in setup..
int16_t d1 = 0, d2 = 0, d3 = 0;
if( cnt > 2000)
{
cnt = 0;
d1 = analogRead(Pot + 2);
d2 = analogRead(8);
d3 = analogRead(10);
//Console.print("pins 6,8,10 --- " );
//Console.print(" ");
// Console.print( d1 );
//Console.print(" ");
//Console.print( d2 );
//Console.print(" ");
//Console.print( d3 );
//Console.print(" ");
//Console.print( CurButton );
//Console.println();
}
}
//figure out a button press, only one button
//at any time with noise rejection. This uses 2 analogue pins
//which multiplexes many buttons on each pin.
void GetButton()
{
int8_t setbounce = 4; //set a debounce here..
static int8_t debounce = setbounce;
static int8_t LastButton = 0;
int8_t thisbut = 0;
int16_t val = analogRead(Buttons); //fire,reload and laser
if( val < 900 )
{
thisbut = 3 - (val >> 8);
}
val = analogRead(Buttons1); //ok, read the fire buttons then..
if( val < 900 )
{
//figure out the button in the fire group
thisbut = (val >> 8) + 4;
}
if( LastButton == thisbut ) debounce--;
else debounce = setbounce;
if( debounce == 0 )
{
CurButton = thisbut;
debounce = setbounce;
}
if( WaitButtonZero && CurButton == 0)
WaitButtonZero = false;
LastButton = thisbut;
}
//make the frerq and count registers count by button.
//this gets called at timer reset time..
ISR(TIMER1_OVF_vect)
{
cnt++;
Duty = FireLaser(); //find the next power level..
OCR1A = Duty;
}
//these three interrupts are for step/dir and spindle pwm.
//used as direct vectors to get the speed required..
//this interrupt is for the step pulses..any step pulses, to
//set a firepulse..
ISR(INT0_vect)
{
//step interrupt...very fast
//if( (OpMode != DIAGS && SpindleOn))
{
FireCount = ReLoad;
FIRE = true;
}
}
//this is for recieving serial data power
//for 16 slice sequences.
ISR(INT1_vect)
{
//count the X steps as well for future use
if( PINB & ( 1 << 7))
SerialIn |= 0x8000;
if( (SerialIn & 0xf00f ) == 0xf00f)
{
// LastSerial = SerialIn & 0x0ff0;
LastSerial = (SerialIn >> 4) & 0xff;
SerialIn = 0;
if( LastSerial < 100 )
{
static unsigned short prev = 0;
if( prev == LastSerial ) //must be a repeated power..
PWMPower = LastSerial;
prev = LastSerial;
}
SerialIn = SerialIn >> 1; //we got a clock, so shift it..
}
}