hi all I think I have everything working have not connected a laser up yet just using a arduino Oscilloscope it seems to be working ok.
I have added my version of the code it has a couple of different bits in for testing only they are commented where they are.
don't use serial when using for laser work it makes it go mad testing only.
Code: Select all
//Arts_laser_control_Leonardo_T1
#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 = 0;
int8_t Spindle = 6; //interrupt, its on pin7
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..
volatile unsigned int OutPower[101];
//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()
{
///////////////////////////////////////////////////////////
//serial for testing switches comment out when not used
//Serial.begin (1200);
// while the serial stream is not open, do nothing
//while (!Serial);
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, 0); //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 );
pinMode( 6, INPUT); //inputs from breakout board
digitalWrite( 6, HIGH );
pinMode( Spindle, INPUT); //inputs from breakout board
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()
{
/*
//diags text for testing and debugging..
//uses if only needed comment out if not used
int16_t d1 = 0, d2 = 0, d3 = 0;
if( cnt > 2000)
{
cnt = 0;
d1 = analogRead(Pot + 2);
d2 = analogRead(8);
d3 = analogRead(10);
Serial.print("pin 6,8,10" );
Serial.print(" ");
Serial.print( d1 );
Serial.print(" ");
Serial.print( d2 );
Serial.print(" ");
Serial.print( d3 );
Serial.print(" ");
Serial.print("current button");
Serial.print(" ");
Serial.print( CurButton );
Serial.print(" ");
Serial.println();
/////////////////////////////////////////////////////////////////////////////////
// /*pwm power to serial monitor use for setting pwm power
//comment out if not needed
Serial.print("pwm");
Serial.print(" ");
Serial.print( PWMPower );
Serial.print(" ");
Serial.println();
}
*/
////////////////////////////////////////////////////////////////////////////
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 == CNCMODE) SpindleCalc(); //only for CNC cut/profile op's..
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 SpindleCalc()
{
if( OpMode == NGRAVE )
return;
if( !SpindleOn ) return;
if( cnt - SpinOffCnt > 225 )
{
SpindleOn = false; //turn it off.
PWMPower = 0;
return;
}
if( SpinOffCnt < SpinOnCnt )
{ //probably a on/off spindle control
if( PWMPower == 0 ) PWMPower = Power;
return;
}
int per = SpinOffCnt - SpinOnCnt;
static int xc = 0;
static int yc = 0;
static int lastpwm = 0;
if( per > 98 ) per = 98;
if( per < 5 ) per = 5;
PWMPower = per;
//switch to base pot power
if( (cnt - SpinOnCnt) > 130 && digitalRead(7) )
PWMPower = Power; //looks like no pwm, just a spindle on command
if( (cnt - SpinOffCnt) > 130 && !digitalRead(7) )
SpindleOn = false; //looks like no pwm, just a spindle on command
}
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 pot power only if the counter says so..
else
if( OpMode == CNCMODE )
ret = PWMPower;
else
ret = OutPower[PWMPower]; //in NGrave mode we return the duty cycle power, or ngrave 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
static int32_t oldval = 0;
//lets only update the power tables if the pot has changed by 2 minimum..
if( PotMode == 0 && abs(oldval - val) > 2 ) // duty cycle.. only mode at present..
{
Power = val / 10.23;
if( Power < 1 ) Power = 1;
if( Power > 100) Power = 100;
Power = 101 - Power;
//fill power tables for NGraving
OutPower[ 0 ] = 0;
double scale = ((double)(31 * Power))/(double)100;
for( int x = 1; x <= 100; x++ )
{
OutPower[ x ] = (unsigned int) (scale * x);
}
}
}
//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)
{
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;
}
//control the spindle
if( PWMPower != 0 ) SpindleOn = true;
else SpindleOn = false;
}
SerialIn = SerialIn >> 1; //we got a clock, so shift it..
}
//and finally a pwm spindle interrupt, basically Reads incoming PWM signal at 50hz..
ISR(INT6_vect)
{
if( PINE & ( 1<<6))
{
SpinOnCnt = cnt;
SpindleOn = true;
}
else
SpinOffCnt = cnt;
return;
}