Add new LASER_PULSE_METHOD thanks to

https://github.com/HakanBastedt/Marlin/tree/laser/Marlin
parent cd0c586e
......@@ -17,6 +17,17 @@
// Uncomment the following if your laser firing pin (not the PWM pin) for two pin control requires a HIGH signal to fire rather than a low (eg Red Sail M300 RS 3040)
/// #define HIGH_TO_FIRE
/// The following define to use the new HakanBasted laser_pulse method to fire laser. It should be more efficient, but it's less tested.
// Thanks for it to HakanBastedt that has implemented it for Marlin at https://github.com/HakanBastedt/Marlin
// Uncomment to enable it *USE AT YOUR OWN RISK*, it should work but it's *NOT WELL TESTED YET*
//#define LASER_PULSE_METHOD
//// In the case that the laserdriver need at least a certain level "LASER_REMAP_INTENSITY"
// to give anything, the intensity can be remapped to start at "LASER_REMAP_INTENSITY"
// At least some CO2-drivers need it, not sure about laserdiode drivers.
#define LASER_REMAP_INTENSITY 7
//// The following defines select which G codes tell the laser to fire. It's OK to uncomment more than one.
#define LASER_FIRE_G1 10 // fire the laser on a G1 move, extinguish when the move ends
#define LASER_FIRE_SPINDLE 11 // fire the laser on M3, extinguish on M5
......
......@@ -14,6 +14,17 @@
// mUVe, buildlog.net and K40 chinese machines uses 2, AMRI ablative uses 1, AMRI SLS uses 2
#define LASER_CONTROL 2
/// The following define to use the new HakanBasted laser_pulse method to fire laser. It should be more efficient, but it's less tested.
// Thanks for it to HakanBastedt that has implemented it for Marlin at https://github.com/HakanBastedt/Marlin
// Uncomment to enable it *USE AT YOUR OWN RISK*, it should work but it's *NOT WELL TESTED YET*
//#define LASER_PULSE_METHOD
//// In the case that the laserdriver need at least a certain level "LASER_REMAP_INTENSITY"
// to give anything, the intensity can be remapped to start at "LASER_REMAP_INTENSITY"
// At least some CO2-drivers need it, not sure about laserdiode drivers.
#define LASER_REMAP_INTENSITY 7
// Uncomment the following if your laser firing pin (not the PWM pin) for two pin control requires a HIGH signal to fire rather than a low (eg Red Sail M300 RS 3040)
/// #define HIGH_TO_FIRE
......
......@@ -25,7 +25,74 @@
laser_t laser;
#if ENABLED(LASER_PULSE_METHOD)
#define bit(x) (1 << x)
#endif
void timer3_init(int pin) {
#if ENABLED(LASER_PULSE_METHOD)
TCCR3A = 0; // clear control register A
TCCR3B = bit(WGM33); // set mode as phase and frequency correct pwm, stop the timer
ICR3 = F_CPU / LASER_PWM / 2; // the counter runs backwards after TOP
TCCR3B &= ~(bit(CS30) | bit(CS31) | bit(CS32)); // Stop timer
TCCR3A |= bit(COM3A1); // Connect pin5 to timer register
DDRE |= bit(PORTE3); // Actually output on pin 5
OCR3A = 0; // Zero duty cycle = OFF
TCCR3B |= bit(CS30); // No prescaler, start timer
// Use timer4 to end laser pulse
/*
Prescaler CS42 CS41 CS40 Range
1 0 0 1 0 - 4.08 msec
8 0 1 0 0 - 32.7 ms <=====
64 0 1 1 0 - 261 ms
256 1 0 0 0 - 1046 ms
1024 1 0 1 0 - 4183 ms
6000 mm/min at 508 dpi = 0.5 ms pulse
300 mm/min at 254 dpi = 20 ms pulse
For the moment a prescaler of 8 is used which
allows up to 32.7 ms pulses with a theoretical
resolution of 0.5 µs.
Waveform generation mode 4: CTC top in OCR4A
============================================
WGN43, WGM42, WGM41, WGM40 = 0, 1, 0, 0
TCCR4A
======
COM4A1, COM4A0 = 0,0 = Normal operation, OC4A disconnected
COM4B1, COM4B0 = 0,0 = Normal operation, OC4B disconnected
COM4C1, COM4C0 = 0,0 = Normal operation, OC4C disconnected
WGM41, WGM40 = 0,0 (See above)
TCCR4B
======
ICN4, IEC4 = 0,0 = Not applicable without input
WGM43, WGM42 = 0,1 (See above)
CS42, CS41, CS40 = 0,1,0 (See above)
CS42, CS41, CS40 = 0,0,0 = Clock stopped
TCCR4C
======
FOC4A, FOC4B, FOS4B = 0,0,0 = Not used
OCR4A
=====
16-bit value when timer overflows = generated interrupt
This is set in laser_pulse()
TIMSK4
======
OCIE4A = 1 = Generate interrupt when timer reach OCR4A
TIFR4
=====
OCF4A: When set, the interrupt will be executed. To clear, write 1 here
When reloading the timer in laser_pulse, an expired interrupt is cleared.
*/
// Prepare laser pulse shutdown timer
TCCR4A = 0;
TCCR4B = bit(WGM42); // CTC
TIMSK4 |= bit(OCIE4A); // Enable interrupt on OCR4A
#else
pinMode(pin, OUTPUT);
analogWrite(pin, 1); // let Arduino setup do it's thing to the PWM pin
......@@ -41,9 +108,30 @@ void timer3_init(int pin) {
ICR3 = labs(F_CPU / LASER_PWM); // set new PWM period
TCCR3B |= 0x01; // start the timer with proper prescaler value
interrupts();
#endif
}
void timer4_init(int pin) {
#if ENABLED(LASER_PULSE_METHOD)
ISR(TIMER4_COMPA_vect)
{
OCR3A = 0; // 0 Duty cycle
// Stop pulse shutdown timer
TCCR4B &= ~(bit(CS40) | bit(CS41) | bit(CS42)); // Stop timer.
}
void laser_pulse(uint32_t ulValue, unsigned long usec)
{
OCR3A = ulValue; // Duty cycle of pulse
// Start timer4 to end pulse
OCR4A = 2*usec; // Ticks until IRQ, "2" comes from prescaler
TCNT4 = 0; // Count from 0
TCCR4B |= bit(CS41); // Start timer
TIFR4 = bit(OCF4A); // Clear any pending interrupt
}
#else // LASER_PULSE_METHOD
void timer4_init(int pin) {
pinMode(pin, OUTPUT);
analogWrite(pin, 1); // let Arduino setup do it's thing to the PWM pin
......@@ -59,10 +147,18 @@ void timer4_init(int pin) {
ICR4 = labs(F_CPU / LASER_PWM); // set new PWM period
TCCR4B |= 0x01; // start the timer with proper prescaler value
interrupts();
}
}
#endif // LASER_PULSE_METHOD
void laser_init()
{
#if ENABLED(LASER_PULSE_METHOD)
// Initialize timers for laser intensity control
// ONLY laser_firing on pin 5. Can't use pin 6 for output, used by timer4.
timer3_init(LASER_FIRING_PIN);
#else
// Initialize timers for laser intensity control
#if LASER_CONTROL == 1
if (LASER_FIRING_PIN == 2 || LASER_FIRING_PIN == 3 || LASER_FIRING_PIN == 5) timer3_init(LASER_FIRING_PIN);
......@@ -72,6 +168,7 @@ void laser_init()
if (LASER_INTENSITY_PIN == 2 || LASER_INTENSITY_PIN == 3 || LASER_INTENSITY_PIN == 5) timer3_init(LASER_INTENSITY_PIN);
if (LASER_INTENSITY_PIN == 6 || LASER_INTENSITY_PIN == 7 || LASER_INTENSITY_PIN == 8) timer4_init(LASER_INTENSITY_PIN);
#endif
#endif // LASER_PULSE_METHOD
#ifdef LASER_PERIPHERALS
digitalWrite(LASER_PERIPHERALS_PIN, HIGH); // Laser peripherals are active LOW, so preset the pin
......@@ -85,7 +182,7 @@ void laser_init()
pinMode(LASER_FIRING_PIN, OUTPUT);
// initialize state to some sane defaults
laser.intensity = 100.0;
laser.intensity = 50.0;
laser.ppm = 0.0;
laser.duration = 0;
laser.status = LASER_OFF;
......@@ -105,17 +202,37 @@ void laser_init()
laser.peel_pause = 0.0;
#endif // MUVE_Z_PEEL
#if !ENABLED(LASER_PULSE_METHOD)
laser_extinguish();
#endif
}
void laser_fire(int intensity = 100.0){
void laser_fire(float intensity = 100.0){
laser.firing = LASER_ON;
laser.last_firing = micros(); // microseconds of last laser firing
if (intensity > 100.0) intensity = 100.0; // restrict intensity between 0 and 100
if (intensity < 0) intensity = 0;
// In the case that the laserdriver need at least a certain level "LASER_REMAP_INTENSITY"
// to give anything, the intensity can be remapped to start at "LASER_REMAP_INTENSITY"
// At least some CO2-drivers need it, not sure about laserdiode drivers.
#if(ENABLED(LASER_REMAP_INTENSITY))
#if LASER_REMAP_INTENSITY != 0
#define OldRange (255.0 - 0.0);
#define NewRange = (255.0 - LASER_REMAP_INTENSITY);
intensity = intensity * NewRange / OldRange + LASER_REMAP_INTENSITY;
#endif
#endif
#if (!ENABLED(LASER_PULSE_METHOD))
pinMode(LASER_FIRING_PIN, OUTPUT);
#endif
#if LASER_CONTROL == 1
#if ENABLED(LASER_PULSE_METHOD)
OCR3A = labs((intensity / 100.0)*(F_CPU / LASER_PWM / 2));
#else
analogWrite(LASER_FIRING_PIN, labs((intensity / 100.0)*(F_CPU / LASER_PWM)));
#endif
#endif
#if LASER_CONTROL == 2
analogWrite(LASER_INTENSITY_PIN, labs((intensity / 100.0)*(F_CPU / LASER_PWM)));
......@@ -130,8 +247,13 @@ void laser_extinguish(){
if (laser.firing == LASER_ON) {
laser.firing = LASER_OFF;
#if ENABLED(LASER_PULSE_METHOD)
OCR3A = 0; // Zero duty cycle = OFF
#else
// Engage the pullup resistor for TTL laser controllers which don't turn off entirely without it.
digitalWrite(LASER_FIRING_PIN, LASER_UNARM);
#endif
laser.time += millis() - (laser.last_firing / 1000);
if (laser.diagnostics) {
......
......@@ -58,6 +58,9 @@ extern laser_t laser;
void laser_init();
void laser_fire(int intensity);
#if ENABLED(LASER_PULSE_METHOD)
void laser_pulse(uint32_t ulValue, unsigned long usec);
#endif
void laser_extinguish();
void laser_update_lifetime();
void laser_set_mode(int mode);
......
......@@ -891,29 +891,21 @@ float junction_deviation = 0.1;
// Calculate steps between laser firings (steps_l) and consider that when determining largest
// interval between steps for X, Y, Z, E, L to feed to the motion control code.
if (laser.mode == RASTER || laser.mode == PULSED) {
#if ENABLED(LASER_PULSE_METHOD)
// Optimizing. Move calculations here rather than in stepper isr
static const float Factor = F_CPU/(LASER_PWM*2*100.0*255.0);
block->laser_raster_intensity_factor = laser.intensity * Factor;
#endif
block->steps_l = labs(block->millimeters*laser.ppm);
if (laser.mode == RASTER) {
for (int i = 0; i < LASER_MAX_RASTER_LINE; i++) {
//Scale the image intensity based on the raster power.
//100% power on a pixel basis is 255, convert back to 255 = 100.
//http://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
int OldRange, NewRange;
float NewValue;
OldRange = (255 - 0);
NewRange = (laser.rasterlaserpower - 7); //7% power on my unit outputs hardly any noticable burn at F3000 on paper, so adjust the raster contrast based off 7 being the lower. 7 still produces burns at slower feed rates, but getting less power than this isn't typically needed at slow feed rates.
NewValue = (float)(((((float)laser.raster_data[i] - 0) * NewRange) / OldRange) + 7);
//If less than 7%, turn off the laser tube.
if(NewValue == 7)
NewValue = 0;
block->laser_raster_data[i] = NewValue;
block->laser_raster_data[i] = laser.raster_data[i];
}
}
} else {
block->steps_l = 0;
}
// NEXTIME
block->step_event_count = max(block->steps[X_AXIS], max(block->steps[Y_AXIS], max(block->steps[Z_AXIS], max(block->steps[E_AXIS], block->steps_l))));
if (laser.diagnostics) {
......
......@@ -108,6 +108,7 @@ typedef struct {
int laser_intensity; // Laser firing instensity in clock cycles for the PWM timer
#if ENABLED(LASER_RASTER)
unsigned char laser_raster_data[LASER_MAX_RASTER_LINE];
float laser_raster_intensity_factor;
#endif
#endif
......
......@@ -429,7 +429,7 @@ ISR(TIMER1_COMPA_vect) {
return;
}
#if ENABLED(LASER)
#if ENABLED(LASER) && (!ENABLED(LASER_PULSE_METHOD))
if (laser.dur != 0 && (laser.last_firing + laser.dur < micros())) {
if (laser.diagnostics) ECHO_LM(INFO,"Laser firing duration elapsed, in interrupt handler");
laser_extinguish();
......@@ -448,8 +448,10 @@ ISR(TIMER1_COMPA_vect) {
counter_X = counter_Y = counter_Z = counter_E = -(current_block->step_event_count >> 1);
#if ENABLED(LASER)
counter_L = counter_X;
#if !ENABLED(LASER_PULSE_METHOD)
laser.dur = current_block->laser_duration;
#endif
#endif
#if ENABLED(COLOR_MIXING_EXTRUDER)
for (uint8_t i = 0; i < DRIVER_EXTRUDERS; i++)
......@@ -496,11 +498,13 @@ ISR(TIMER1_COMPA_vect) {
if (current_block->laser_mode == CONTINUOUS && current_block->laser_status == LASER_ON) {
laser_fire(current_block->laser_intensity);
}
#if !ENABLED(LASER_PULSE_METHOD)
if (current_block->laser_status == LASER_OFF) {
if (laser.diagnostics) ECHO_LM(INFO,"Laser status set to off, in interrupt handler");
laser_extinguish();
}
#endif
#endif
// Take multiple steps per interrupt (For high speed moves)
for (uint8_t i = 0; i < step_loops; i++) {
......@@ -600,7 +604,13 @@ ISR(TIMER1_COMPA_vect) {
counter_L += current_block->steps_l;
if (counter_L > 0) {
if (current_block->laser_mode == PULSED && current_block->laser_status == LASER_ON) { // Pulsed Firing Mode
#if ENABLED(LASER_PULSE_METHOD)
uint32_t ulValue = current_block->laser_raster_intensity_factor * 255;
laser_pulse(ulValue, current_block->laser_duration);
laser.time += current_block->laser_duration/1000;
#else
laser_fire(current_block->laser_intensity);
#endif
if (laser.diagnostics) {
ECHO_MV("X: ", counter_X);
ECHO_MV("Y: ", counter_Y);
......@@ -609,9 +619,17 @@ ISR(TIMER1_COMPA_vect) {
}
#if ENABLED(LASER_RASTER)
if (current_block->laser_mode == RASTER && current_block->laser_status == LASER_ON) { // Raster Firing Mode
#if ENABLED(LASER_PULSE_METHOD)
uint32_t ulValue = current_block->laser_raster_intensity_factor *
current_block->laser_raster_data[counter_raster];
laser_pulse(ulValue, current_block->laser_duration);
counter_raster++;
laser.time += current_block->laser_duration/1000;
#else
// For some reason, when comparing raster power to ppm line burns the rasters were around 2% more powerful
// going from darkened paper to burning through paper.
laser_fire(current_block->laser_raster_data[counter_raster]);
#endif
if (laser.diagnostics) {
ECHO_MV("Pixel: ", (float)current_block->laser_raster_data[counter_raster]);
}
......@@ -620,10 +638,12 @@ ISR(TIMER1_COMPA_vect) {
#endif // LASER_RASTER
counter_L -= current_block->step_event_count;
}
#if !ENABLED(LASER_PULSE_METHOD)
if (current_block->laser_duration != 0 && (laser.last_firing + current_block->laser_duration < micros())) {
if (laser.diagnostics) ECHO_LM(INFO, "Laser firing duration elapsed, in interrupt fast loop");
laser_extinguish();
}
#endif
#endif // LASER
......@@ -734,6 +754,11 @@ ISR(TIMER1_COMPA_vect) {
if (step_events_completed >= current_block->step_event_count) {
current_block = NULL;
plan_discard_current_block();
#if ENABLED(LASER) && ENABLED(LASER_PULSE_METHOD)
if (current_block->laser_mode == CONTINUOUS && current_block->laser_status == LASER_ON) {
laser_extinguish();
}
#endif
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment