GRBL 0.9/1.1 mod For 4-Wire Motor Stepper 28BYJ-48

Tag

, , , ,

This is my modification of GRBL 0.9/1.1 for CNC plotter with 4 wire motor stepper, unipolar or bipolar. And this time I do the article in English (yay), but you should know that my english isn’t good, please bear with me 🙂

This mod could be applicated to 4 wire unipolar motor stepper like 28-BYJ-48 with ULN2003 driver, or using 4 wire bipolar stepper. What i’m using are 2x 28-BYJ-48 for X-Y axis, and 4-wire bipolar cd/dvd-drive motor stepper for Z-axis. I’ve done it with GRBL 0.9 and 1.1, it’s all work. But I need to disable some stuff, because there is no enough space. I think we can reduce the mod for more space if needed, I didn’t do it because I want to keep easy-ness and clarity.

I did the experiment with arduino nano. The stepper will use 4×3 = 12pin. Excluding nano significant pin (Rx/Tx), we will have about 17pin (D2-D13, A0-A5). In these experiment I only use those 12pin for controlling XYZ only. I don’t use any shield nor any switch etc.

The mod are in 3 files, cpu_map_atmega328p.h, stepper.c, and config.h. It’s already quite a time since i did this, so I hope did not miss anything here 😀 . The mod here are for GRBL 0.9, but you can use the same principle for GRBL 1.1. My reason using GRBL 0.9 because I love Grbl_Controller and it work best with 0.9. So here are the mod:

cpu_map_atmega328p.h

I added virtual mapping for avoiding clash pin with the new used pin. I basically have no time to trace all the code 😀 and with these it will be easier to go back to original code, in case I want to enable some function in the future.

/*
cpu_map_atmega328p.h - CPU and pin mapping configuration file
Part of Grbl

Copyright (c) 2012-2015 Sungeun K. Jeon

Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/

/* Grbl officially supports the Arduino Uno, but the other supplied pin mappings are
supplied by users, so your results may vary. This cpu_map file serves as a central
pin mapping settings file for AVR 328p used on the Arduino Uno. */

#ifdef GRBL_PLATFORM
#error "cpu_map already defined: GRBL_PLATFORM=" GRBL_PLATFORM
#endif

#define GRBL_PLATFORM "Atmega328p"

/* added modification for using Z motor stepper, nw_xsn - 10-2019 */

// CUSTOM MAP FOR VIRTUAL REGISTER
static int VDDRD = 0;
static int VDDRB = 0;
static int VDDRC = 0;
static int VPORTD = 0;
static int VPORTC = 0;
static int VPORTB = 0;
static int VPIND = 0;
static int VPINC = 0;
static int VPINB = 0;

// Define serial port pins and interrupt vectors.
#define SERIAL_RX USART_RX_vect
#define SERIAL_UDRE USART_UDRE_vect

// Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEP_DDR VDDRD
#define STEP_PORT VPORTD
#define X_STEP_BIT 2 // Uno Digital Pin 2
#define Y_STEP_BIT 3 // Uno Digital Pin 3
#define Z_STEP_BIT 4 // Uno Digital Pin 4
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits

// Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR VDDRD
#define DIRECTION_PORT VPORTD
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits

// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR VDDRB
#define STEPPERS_DISABLE_PORT VPORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)

// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL).
#define LIMIT_DDR VDDRB
#define LIMIT_PIN VPINB
#define LIMIT_PORT VPORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define Z_LIMIT_BIT 4 // Uno Digital Pin 12
#else
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#endif
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register

// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR VDDRB
#define SPINDLE_ENABLE_PORT VPORTB
// Z Limit pin and spindle PWM/enable pin swapped to access hardware PWM on Pin 11.
#ifdef VARIABLE_SPINDLE
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
// If enabled, spindle direction pin now used as spindle enable, while PWM remains on D11.
#define SPINDLE_ENABLE_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#else
#define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11
#endif
#else
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#endif
#ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN
#define SPINDLE_DIRECTION_DDR VDDRB
#define SPINDLE_DIRECTION_PORT VPORTB
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#endif

// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR VDDRC
#define COOLANT_FLOOD_PORT VPORTC
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR VDDRC
#define COOLANT_MIST_PORT VPORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
#endif

// Define user-control controls (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_DDR VDDRC
#define CONTROL_PIN VPINC
#define CONTROL_PORT VPORTC

// I'm just too lazy to trace this, so I just move it away from XYZ pin
#define RESET_BIT 10 //0 Uno Analog Pin 0
#define FEED_HOLD_BIT 11 //1 Uno Analog Pin 1
#define CYCLE_START_BIT 12 //2 Uno Analog Pin 2
#define SAFETY_DOOR_BIT 13//1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT1_vect
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
// CONTROL DISABLED TO DISABLE ALARM
//#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
#define CONTROL_MASK 0

#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.

// Define probe switch input pin.
#define PROBE_DDR VDDRC
#define PROBE_PIN VPINC
#define PROBE_PORT VPORTC
#define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT)

// Start of PWM & Stepper Enabled Spindle
#ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables
#define PWM_MAX_VALUE 255.0
#define TCCRA_REGISTER TCCR2A
#define TCCRB_REGISTER TCCR2B
#define OCR_REGISTER OCR2A

#define COMB_BIT COM2A1
#define WAVE0_REGISTER WGM20
#define WAVE1_REGISTER WGM21
#define WAVE2_REGISTER WGM22
#define WAVE3_REGISTER WGM23

// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#define SPINDLE_PWM_DDR VDDRB
#define SPINDLE_PWM_PORT VPORTB
#define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11
#endif // End of VARIABLE_SPINDLE

stepper.c

/*
stepper.c - stepper motor driver: executes motion plans using stepper motors
Part of Grbl

Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud

Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/

#include "grbl.h"

// Some useful constants.
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25
#define RAMP_ACCEL 0
#define RAMP_CRUISE 1
#define RAMP_DECEL 2

// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level
// frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin
// starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
// be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
// Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
// NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency.
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
// and timer accuracy. Do not alter these settings unless you know what you are doing.
#define MAX_AMASS_LEVEL 3
// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
#define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
#define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4)
#define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)

// Stores the planner block Bresenham algorithm execution data for the segments in the segment
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
// NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be
// discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this
// data for its own use.
typedef struct {
uint8_t direction_bits;
uint32_t steps[N_AXIS];
uint32_t step_event_count;
} st_block_t;
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];

// Primary stepper segment ring buffer. Contains small, short line segments for the stepper
// algorithm to execute, which are "checked-out" incrementally from the first block in the
// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by
// the planner, where the remaining planner block steps still can.
typedef struct {
uint16_t n_step; // Number of step events to be executed for this segment
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
#else
uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
#endif
} segment_t;
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];

// Stepper ISR data struct. Contains the running data for the main stepper ISR.
typedef struct {
// Used by the bresenham line algorithm
uint32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
counter_z;
#ifdef STEP_PULSE_DELAY
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
#endif

uint8_t execute_step; // Flags step execution for each interrupt.
uint8_t step_pulse_time; // Step pulse reset time after step rise
uint8_t step_outbits; // The next stepping-bits to be output
uint8_t dir_outbits;
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
uint32_t steps[N_AXIS];
#endif

uint16_t step_count; // Steps remaining in line segment motion
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
st_block_t *exec_block; // Pointer to the block data for the segment being executed
segment_t *exec_segment; // Pointer to the segment being executed
} stepper_t;
static stepper_t st;

// Step segment ring buffer indices
static volatile uint8_t segment_buffer_tail;
static uint8_t segment_buffer_head;
static uint8_t segment_next_head;

// Step and direction port invert masks.
static uint8_t step_port_invert_mask;
static uint8_t dir_port_invert_mask;

// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
static volatile uint8_t busy;

// Pointers for the step segment being prepped from the planner buffer. Accessed only by the
// main program. Pointers may be planning segments or planner blocks ahead of what being executed.
static plan_block_t *pl_block; // Pointer to the planner block being prepped
static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped

// Segment preparation data struct. Contains all the necessary information to compute new segments
// based on the current executing planner block.
typedef struct {
uint8_t st_block_index; // Index of stepper common data block being prepped
uint8_t flag_partial_block; // Flag indicating the last block completed. Time to load a new one.

float steps_remaining;
float step_per_mm; // Current planner block step/millimeter conversion scalar
float req_mm_increment;
float dt_remainder;

uint8_t ramp_type; // Current segment ramp state
float mm_complete; // End of velocity profile from end of current planner block in (mm).
// NOTE: This value must coincide with a step(no mantissa) when converted.
float current_speed; // Current speed at the end of the segment buffer (mm/min)
float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min)
float exit_speed; // Exit speed of executing block (mm/min)
float accelerate_until; // Acceleration ramp end measured from end of block (mm)
float decelerate_after; // Deceleration ramp start measured from end of block (mm)
} st_prep_t;
static st_prep_t prep;

/* added modification for using Z motor stepper, nw_xsn - 10-2019
USING PIN
X: D2-D5 PD2-PD5 PORTD=0B00111100; D0-D1 RX/TX
Y: A0-A3 PC0-PC3 PORTC=0B001111;
Z: D8-D11 PB0-PB3 PORTB=0B001111;
*/

// MAY NO ENOUGH MEMORY

#define PinX0 2
#define PinX1 3
#define PinX2 4
#define PinX3 5
//analog pins are 14 through 19
#define PinY0 14 //A0-A3
#define PinY1 15
#define PinY2 16
#define PinY3 17
#define PinZ0 8
#define PinZ1 9
#define PinZ2 10
#define PinZ3 11
#define STEPX_MASK 0B00111100 //60 PORTD
#define STEPY_MASK 0B001111 //15 PORTC
#define STEPZ_MASK 0B001111 //15 PORTB
#define DIRX_MASK 0B00111100
#define DIRY_MASK 0B001111
#define DIRZ_MASK 0B001111
#define PORTX PORTD
#define PORTY PORTC
#define PORTZ PORTB
#define DDRX DDRD
#define DDRY DDRC
#define DDRZ DDRB

static int _stepX = 0;static int _stepY = 0;static int _stepZ = 0;
static int dir = 1;// gre
static int c = 0;

/*
pinMode(PinX0, OUTPUT);
pinMode(PinX1, OUTPUT);
pinMode(PinX2, OUTPUT);
pinMode(PinX3, OUTPUT);
pinMode(PinY0, OUTPUT);
pinMode(PinY1, OUTPUT);
pinMode(PinY2, OUTPUT);
pinMode(PinY3, OUTPUT);
pinMode(PinZ0, OUTPUT);
pinMode(PinZ1, OUTPUT);
pinMode(PinZ2, OUTPUT);
pinMode(PinZ3, OUTPUT);
*/

void stepperSetDirection(){
/*
pinMode(PinX0, 1);
pinMode(PinX1, 1);
pinMode(PinX2, 1);
pinMode(PinX3, 1);
pinMode(PinY0, 1);
pinMode(PinY1, 1);
pinMode(PinY2, 1);
pinMode(PinY3, 1);
pinMode(PinZ0, 1);
pinMode(PinZ1, 1);
pinMode(PinZ2, 1);
pinMode(PinZ3, 1);
*/

// uncomment this and comment above lines if we need more space
// CLEAR AND SET DIR BIT
DDRX = (DDRX & ~DIRX_MASK) | DIRX_MASK;
DDRY = (DDRY & ~DIRY_MASK) | DIRY_MASK;
DDRZ = (DDRZ & ~DIRZ_MASK) | DIRZ_MASK;
}

// STEPPER X Y USING UNIPOLAR 28BYJ-48 ULN2003
/*
STEP C0 C1 C2 C3
1 1 0 0 0
2 1 1 0 0
3 0 1 0 0
4 0 1 1 0
5 0 0 1 0
6 0 0 1 1
7 0 0 0 1
8 1 0 0 1
*/

void stepperXstep(dir) {
//for(int i=0;i<4;i++)
{
switch (_stepX) {
case 0:
/* IF WE GOT ENOGH SPACE WE COULD USE THIS, EASIER TO READ
digitalWrite(PinX0, 1);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 0);
*/
// CLEAR THE TARGET BIT FIRST, KEEP OTHER BIT THEN ORED WITH THE NEW BIT
// PORTD = PORTD & 0B000011; -> PORTD = PORTD & ~STEPX_MASK
// PORTD = PORTD | 0B100000;
PORTX = (PORTX & ~STEPX_MASK) | 0B100000;
break;
case 1:
/*
digitalWrite(PinX0, 1);
digitalWrite(PinX1, 1);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 0);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B110000;
break;
case 2:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 1);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 0);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B010000;
break;
case 3:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 1);
digitalWrite(PinX2, 1);
digitalWrite(PinX3, 0);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B011000;
break;
case 4:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 1);
digitalWrite(PinX3, 0);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B001000;
break;
case 5:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 1);
digitalWrite(PinX3, 1);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B001100;
break;
case 6:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 1);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B000100;
break;
case 7:
/*
digitalWrite(PinX0, 1);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 1);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B101000;
break;
default:
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 0);
*/
PORTX = (PORTX & ~STEPX_MASK) | 0B000000;
break;
}
if (dir>0) {
_stepX++;
c++;
} else {
_stepX--;
c--;
}
if (_stepX > 7) {
_stepX = 0;
}
if (_stepX < 0) {
_stepX = 7;
}

delay_ms(5); // BYJ48 DATASHEET SAYS 100HZ -> 10ms MAX, USING 5ms HERE SEEM OK

}
}

void stepperYstep(dir) {
//for(int i=0;i<4;i++)
{
switch (_stepY) {
case 0:
/*digitalWrite(PinY0, 1);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B1000;
break;
case 1:
/*
digitalWrite(PinY0, 1);
digitalWrite(PinY1, 1);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B1100;
break;
case 2:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 1);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0100;
break;
case 3:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 1);
digitalWrite(PinY2, 1);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0110;
break;
case 4:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 1);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0010;
break;
case 5:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 1);
digitalWrite(PinY3, 1);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0011;
break;
case 6:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 1);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0001;
break;
case 7:
/*
digitalWrite(PinY0, 1);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 1);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B1001;
break;
default:
/*
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 0);
*/
PORTY = (PORTY & ~STEPY_MASK) | 0B0000;
break;
}
if (dir>0) {
_stepY++;
c++;
} else {
_stepY--;
c--;
}
if (_stepY > 7) {
_stepY = 0;
}
if (_stepY < 0) {
_stepY = 7;
}

delay_ms(5);

}
}

// STEPPER Z USING H-BRIDGE / BIPOLAR
/*
* The sequence of control signals for 4 control wires is as follows:
*
* Step C0 C1 C2 C3
* 1 1 0 1 0
* 2 0 1 1 0
* 3 0 1 0 1
* 4 1 0 0 1
*
*/
void stepperZstep(dir) {
//for(int i=0;i<4;i++)
{
switch (_stepZ) {
case 0:
/*
digitalWrite(PinZ0, 1);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 1);
digitalWrite(PinZ3, 0);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B1010;
break;
case 1:
/*
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 1);
digitalWrite(PinZ2, 1);
digitalWrite(PinZ3, 0);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B0110;
break;
case 2:
/*
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 1);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 1);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B0101;
break;
case 3:
/*
digitalWrite(PinZ0, 1);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 1);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B1001;
break;
case 4:
/*
digitalWrite(PinZ0, 1);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 1);
digitalWrite(PinZ3, 0);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B1010;
break;
case 5:
/*
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 1);
digitalWrite(PinZ2, 1);
digitalWrite(PinZ3, 0);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B0110;
break;
case 6:
/*
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 1);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 1);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B0101;
break;
case 7:
/*
digitalWrite(PinZ0, 1);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 1);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B1001;
break;
default:
/*
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 0);
*/
PORTZ = (PORTZ & ~STEPZ_MASK) | 0B0000;
break;
}
if (dir>0) {
_stepZ++;
c++;
} else {
_stepZ--;
c--;
}
if (_stepZ > 7) {
_stepZ = 0;
}
if (_stepZ < 0) {
_stepZ = 7;
}

delay_ms(5); // ADJUST TO YOUR MOTOR STEPPER

}
}

void stepperOff(){
/*
digitalWrite(PinX0, 0);
digitalWrite(PinX1, 0);
digitalWrite(PinX2, 0);
digitalWrite(PinX3, 0);
digitalWrite(PinY0, 0);
digitalWrite(PinY1, 0);
digitalWrite(PinY2, 0);
digitalWrite(PinY3, 0);
digitalWrite(PinZ0, 0);
digitalWrite(PinZ1, 0);
digitalWrite(PinZ2, 0);
digitalWrite(PinZ3, 0);
*/
// uncomment this and comment above lines if we need more space
// PORTD = PORTD & 0B000011; -> PORTD = PORTD & ~STEPX_MASK
PORTX = PORTX & ~STEPX_MASK;
PORTY = PORTY & ~STEPY_MASK;
PORTZ = PORTZ & ~STEPZ_MASK;

}

void parseStepsBit(int LSTEP_PORT){// saving space

if (bit_istrue(LSTEP_PORT,(1<<X_STEP_BIT)))
{
if (bit_istrue(LSTEP_PORT,(1<<X_DIRECTION_BIT)))
{stepperXstep(-1);
//print_uint8_base2_ndigit(LSTEP_PORT,8);printString("\n");
}
else
{stepperXstep(1);
//print_uint8_base2_ndigit(LSTEP_PORT,8);printString("\n");
}
}

if (bit_istrue(LSTEP_PORT,(1<<Y_STEP_BIT)))
{
if (bit_istrue(LSTEP_PORT,(1<<Y_DIRECTION_BIT)))
{stepperYstep(-1);}
else
{stepperYstep(1);}
}

if (bit_istrue(LSTEP_PORT,(1<<Z_STEP_BIT)))
{
if (bit_istrue(LSTEP_PORT,(1<<Z_DIRECTION_BIT)))
{stepperZstep(-1);}
else
{stepperZstep(1);}
}

}




/* BLOCK VELOCITY PROFILE DEFINITION
__________________________
/| |\ _________________ ^
/ | | \ /| |\ |
/ | | \ / | | \ s
/ | | | | | \ p
/ | | | | | \ e
+-----+------------------------+---+--+---------------+----+ e
| BLOCK 1 ^ BLOCK 2 | d
|
time -----> EXAMPLE: Block 2 entry speed is at max junction velocity

The planner block buffer is planned assuming constant acceleration velocity profiles and are
continuously joined at block junctions as shown above. However, the planner only actively computes
the block entry speeds for an optimal velocity plan, but does not compute the block internal
velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the
stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise-
deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
triangle(no cruise).

maximum_speed (< nominal_speed) -> +
+--------+ <- maximum_speed (= nominal_speed) /|\
/ \ / | \
current_speed -> + \ / | + <- exit_speed
| + <- exit_speed / | |
+-------------+ current_speed -> +----+--+
time --> ^ ^ ^ ^
| | | |
decelerate_after(in mm) decelerate_after(in mm)
^ ^ ^ ^
| | | |
accelerate_until(in mm) accelerate_until(in mm)

The step segment buffer computes the executing block velocity profile and tracks the critical
parameters for the stepper algorithm to accurately trace the profile. These critical parameters
are shown and defined in the above illustration.
*/

// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
// enabled. Startup init and limits call this function but shouldn't start the cycle.
void st_wake_up()
{
// Enable stepper drivers.
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE))
{
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
stepperOff();
}
else
{
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
stepperOff();
}

if (sys.state & (STATE_CYCLE | STATE_HOMING)){
// Initialize stepper output bits
st.dir_outbits = dir_port_invert_mask;
st.step_outbits = step_port_invert_mask;

// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
#ifdef STEP_PULSE_DELAY
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
// Set delay between direction pin write and step command.
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
#else // Normal operation
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
#endif

// Enable Stepper Driver Interrupt
TIMSK1 |= (1<<OCIE1A);
}
}

// Stepper shutdown
void st_go_idle()
{
// Disable Stepper Driver Interrupt. Allow Stepper Port Reset Interrupt to finish, if active.
TIMSK1 &= ~(1<<OCIE1A); // Disable Timer1 interrupt
TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Reset clock to no prescaling.
busy = false;

// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
bool pin_state = false; // Keep enabled.
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm) && sys.state != STATE_HOMING) {
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
delay_ms(settings.stepper_idle_lock_time);
pin_state = true; // Override. Disable steppers.
}
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { pin_state = !pin_state; } // Apply pin invert.
if (pin_state) {
STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
stepperOff();
}
else {
STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
stepperOff();
}
}

/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. Grbl employs
the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves.
Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical
round-off errors and only requires fast integer counters, meaning low computational overhead
and maximizing the Arduino's capabilities. However, the downside of the Bresenham algorithm
is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step
pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is
particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but
is usually not a physical problem at higher frequencies, although audible.
To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis
Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies,
AMASS artificially increases the Bresenham resolution without effecting the algorithm's
innate exactness. AMASS adapts its resolution levels automatically depending on the step
frequency to be executed, meaning that for even lower step frequencies the step smoothing
level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham
step count for each AMASS level. For example, for a Level 1 step smoothing, we bit shift
the Bresenham step event count, effectively multiplying it by 2, while the axis step counts
remain the same, and then double the stepper ISR frequency. In effect, we are allowing the
non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is
stepping every two ISR ticks, rather than every ISR tick in the traditional sense. At AMASS
Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any
of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the
stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing
issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but
in fact, more efficiently utilizes unused CPU cycles overall throughout all configurations.
AMASS retains the Bresenham algorithm exactness by requiring that it always executes a full
Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four
intermediate steps must be completed such that baseline Bresenham (Level 0) count is always
retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed.
Although the AMASS Levels are in reality arbitrary, where the baseline Bresenham counts can
be multiplied by any integer value, multiplication by powers of two are simply used to ease
CPU overhead with bitshift integer operations.
This interrupt is simple and dumb by design. All the computational heavy-lifting, as in
determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments,
defined as constant velocity over n number of steps, from the step segment buffer and then
executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This
ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port
after each pulse. The bresenham line tracer algorithm controls all stepper outputs
simultaneously with these two interrupts.

NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
which for Grbl must be less than 33.3usec (@30kHz ISR rate). Oscilloscope measured time in
ISR is 5usec typical and 25usec maximum, well below requirement.
NOTE: This ISR expects at least one step to be executed per segment.
*/
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
// int8 variables and update position counters only when a segment completes. This can get complicated
// with probing and homing cycles that require true real-time positions.
ISR(TIMER1_COMPA_vect)
{
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt

// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);

// Then pulse the stepping pins
#ifdef STEP_PULSE_DELAY
st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
#else // Normal operation
STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
// PARSE STEPS BIT
// IT SEEM THE SLOW BYJ48 CAN'T HANDLE VARIABLE FAST PULSE WELL, MOVE MAY NOT ACCURATE
parseStepsBit(STEP_PORT);

#endif

// Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
// exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
TCNT0 = st.step_pulse_time; // Reload Timer0 counter
TCCR0B = (1<<CS01); // Begin Timer0. Full speed, 1/8 prescaler

busy = true;
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
// NOTE: The remaining code in this ISR will finish before returning to main program.

// If there is no step segment, attempt to pop one from the stepper buffer
if (st.exec_segment == NULL) {
// Anything in the buffer? If so, load and initialize next step segment.
if (segment_buffer_head != segment_buffer_tail) {
// Initialize new step segment and load number of steps to execute
st.exec_segment = &segment_buffer[segment_buffer_tail];

#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// With AMASS is disabled, set timer prescaler for segments with slow step frequencies (< 250Hz).
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
#endif

// Initialize step segment timing per step and load number of steps to execute.
OCR1A = st.exec_segment->cycles_per_tick;
st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
// If the new segment starts a new planner block, initialize stepper variables and counters.
// NOTE: When the segment data index changes, this indicates a new planner block.
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
st.exec_block_index = st.exec_segment->st_block_index;
st.exec_block = &st_block_buffer[st.exec_block_index];

// Initialize Bresenham line and distance counters
st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
}
st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask;

#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level;
st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level;
#endif

} else {
// Segment buffer empty. Shutdown.
st_go_idle();
bit_true_atomic(sys_rt_exec_state,EXEC_CYCLE_STOP); // Flag main program for cycle end
return; // Nothing to do but exit.
}
}

// Check probing state.
probe_state_monitor();

// Reset step out bits.
st.step_outbits = 0;

// Execute step displacement profile by Bresenham line algorithm
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_x += st.steps[X_AXIS];
#else
st.counter_x += st.exec_block->steps[X_AXIS];
#endif
if (st.counter_x > st.exec_block->step_event_count) {
st.step_outbits |= (1<<X_STEP_BIT);
st.counter_x -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
else { sys.position[X_AXIS]++; }
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_y += st.steps[Y_AXIS];
#else
st.counter_y += st.exec_block->steps[Y_AXIS];
#endif
if (st.counter_y > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Y_STEP_BIT);
st.counter_y -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
else { sys.position[Y_AXIS]++; }
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_z += st.steps[Z_AXIS];
#else
st.counter_z += st.exec_block->steps[Z_AXIS];
#endif
if (st.counter_z > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Z_STEP_BIT);
st.counter_z -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
else { sys.position[Z_AXIS]++; }
}

// During a homing cycle, lock out and prevent desired axes from moving.
if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; }

st.step_count--; // Decrement step events count
if (st.step_count == 0) {
// Segment is complete. Discard current segment and advance segment indexing.
st.exec_segment = NULL;
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
}

st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask
busy = false;
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
}

/* The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
pulse. This should always trigger before the next Timer1 COMPA interrupt and independently
finish, if Timer1 is disabled after completing a move.
NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
a few microseconds, if they execute right before one another. Not a big deal, but can
cause issues at high step rates if another high frequency asynchronous interrupt is
added to Grbl.
*/
// This interrupt is enabled by ISR_TIMER1_COMPAREA when it sets the motor port bits to execute
// a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
// completing one step cycle.
ISR(TIMER0_OVF_vect)
{
// Reset stepping pins (leave the direction pins)
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
stepperOff();

TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed.
}
#ifdef STEP_PULSE_DELAY
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
// initiated after the STEP_PULSE_DELAY time period has elapsed. The ISR TIMER2_OVF interrupt
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
// The new timing between direction, step pulse, and step complete events are setup in the
// st_wake_up() routine.
ISR(TIMER0_COMPA_vect)
{
STEP_PORT = st.step_bits; // Begin step pulse.
// PARSE STEPS BIT
parseStepsBit(STEP_PORT);
}
#endif

// Generates the step and direction port invert masks used in the Stepper Interrupt Driver.
void st_generate_step_dir_invert_masks()
{
uint8_t idx;
step_port_invert_mask = 0;
dir_port_invert_mask = 0;
for (idx=0; idx<N_AXIS; idx++) {
if (bit_istrue(settings.step_invert_mask,bit(idx))) { step_port_invert_mask |= get_step_pin_mask(idx); }
if (bit_istrue(settings.dir_invert_mask,bit(idx))) { dir_port_invert_mask |= get_direction_pin_mask(idx); }
}
}

// Reset and clear stepper subsystem variables
void st_reset()
{
// Initialize stepper driver idle state.
st_go_idle();

// Initialize stepper algorithm variables.
memset(&prep, 0, sizeof(st_prep_t));
memset(&st, 0, sizeof(stepper_t));
st.exec_segment = NULL;
pl_block = NULL; // Planner block pointer used by segment buffer
segment_buffer_tail = 0;
segment_buffer_head = 0; // empty = tail
segment_next_head = 1;
busy = false;

st_generate_step_dir_invert_masks();

// Initialize step and direction port pins.
STEP_PORT = (STEP_PORT & ~STEP_MASK) | step_port_invert_mask;
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | dir_port_invert_mask;
stepperSetDirection();
stepperOff();
}

// Initialize and start the stepper motor subsystem
void stepper_init()
{
// Configure step and direction interface pins
STEP_DDR |= STEP_MASK;
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
DIRECTION_DDR |= DIRECTION_MASK;
stepperSetDirection();
stepperOff();

// Configure Timer 1: Stepper Driver Interrupt
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
TCCR1B |= (1<<WGM12);
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0)); // Disconnect OC1 output
// TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Set in st_go_idle().
// TIMSK1 &= ~(1<<OCIE1A); // Set in st_go_idle().

// Configure Timer 0: Stepper Port Reset Interrupt
TIMSK0 &= ~((1<<OCIE0B) | (1<<OCIE0A) | (1<<TOIE0)); // Disconnect OC0 outputs and OVF interrupt.
TCCR0A = 0; // Normal operation
TCCR0B = 0; // Disable Timer0 until needed
TIMSK0 |= (1<<TOIE0); // Enable Timer0 overflow interrupt
#ifdef STEP_PULSE_DELAY
TIMSK0 |= (1<<OCIE0A); // Enable Timer0 Compare Match A interrupt
#endif
}

// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters()
{
if (pl_block != NULL) { // Ignore if at start of a new block.
prep.flag_partial_block = true;
pl_block->entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed.
pl_block = NULL; // Flag st_prep_segment() to load new velocity profile.
}
}

/* Prepares step segment buffer. Continuously called from main program.

The segment buffer is an intermediary buffer interface between the execution of steps
by the stepper algorithm and the velocity profiles generated by the planner. The stepper
algorithm only executes steps within the segment buffer and is filled by the main program
when steps are "checked-out" from the first block in the planner buffer. This keeps the
step execution and planning optimization processes atomic and protected from each other.
The number of steps "checked-out" from the planner buffer and the number of segments in
the segment buffer is sized and computed such that no operation in the main program takes
longer than the time it takes the stepper algorithm to empty it before refilling it.
Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
NOTE: Computation units are in steps, millimeters, and minutes.
*/
void st_prep_buffer()
{

if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Check if we still need to generate more segments for a motion suspend.
if (prep.current_speed == 0.0) { return; } // Nothing to do. Bail.
}

while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.

// Determine if we need to load a new planner block or if the block has been replanned.
if (pl_block == NULL) {
pl_block = plan_get_current_block(); // Query planner for a queued block
if (pl_block == NULL) { return; } // No planner blocks. Exit.

// Check if the segment buffer completed the last planner block. If so, load the Bresenham
// data for the block. If not, we are still mid-block and the velocity profile was updated.
if (prep.flag_partial_block) {
prep.flag_partial_block = false; // Reset flag
} else {
// Increment stepper common data index to store new planner block data.
if ( ++prep.st_block_index == (SEGMENT_BUFFER_SIZE-1) ) { prep.st_block_index = 0; }

// Prepare and copy Bresenham algorithm segment data from the new planner block, so that
// when the segment buffer completes the planner block, it may be discarded when the
// segment buffer finishes the prepped block, but the stepper ISR is still executing it.
st_prep_block = &st_block_buffer[prep.st_block_index];
st_prep_block->direction_bits = pl_block->direction_bits;
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
st_prep_block->step_event_count = pl_block->step_event_count;
#else
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
// level, such that we never divide beyond the original data anywhere in the algorithm.
// If the original data is divided, we can lose a step from integer roundoff.
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS] << MAX_AMASS_LEVEL;
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS] << MAX_AMASS_LEVEL;
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS] << MAX_AMASS_LEVEL;
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
#endif

// Initialize segment buffer data for generating the segments.
prep.steps_remaining = pl_block->step_event_count;
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm;

prep.dt_remainder = 0.0; // Reset for new planner block

if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Override planner block entry speed and enforce deceleration during feed hold.
prep.current_speed = prep.exit_speed;
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
}
else { prep.current_speed = sqrt(pl_block->entry_speed_sqr); }
}

/* ---------------------------------------------------------------------------------
Compute the velocity profile of a new planner block based on its entry and exit
speeds, or recompute the profile of a partially-completed planner block if the
planner has updated it. For a commanded forced-deceleration, such as from a feed
hold, override the planner velocities and decelerate to the target exit speed.
*/
prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block.
float inv_2_accel = 0.5/pl_block->acceleration;
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // [Forced Deceleration to Zero Velocity]
// Compute velocity profile parameters for a feed hold in-progress. This profile overrides
// the planner block profile, enforcing a deceleration to zero speed.
prep.ramp_type = RAMP_DECEL;
// Compute decelerate distance relative to end of block.
float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr;
if (decel_dist < 0.0) {
// Deceleration through entire planner block. End of feed hold is not in this block.
prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
} else {
prep.mm_complete = decel_dist; // End of feed hold.
prep.exit_speed = 0.0;
}
} else { // [Normal Operation]
// Compute or recompute velocity profile parameters of the prepped planner block.
prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp.
prep.accelerate_until = pl_block->millimeters;
prep.exit_speed = plan_get_exec_block_exit_speed();
float exit_speed_sqr = prep.exit_speed*prep.exit_speed;
float intersect_distance =
0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr));
if (intersect_distance > 0.0) {
if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types
// NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0.
prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr);
if (prep.decelerate_after < intersect_distance) { // Trapezoid type
prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr);
if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) {
// Cruise-deceleration or cruise-only type.
prep.ramp_type = RAMP_CRUISE;
} else {
// Full-trapezoid or acceleration-cruise types
prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr);
}
} else { // Triangle type
prep.accelerate_until = intersect_distance;
prep.decelerate_after = intersect_distance;
prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr);
}
} else { // Deceleration-only type
prep.ramp_type = RAMP_DECEL;
// prep.decelerate_after = pl_block->millimeters;
prep.maximum_speed = prep.current_speed;
}
} else { // Acceleration-only type
prep.accelerate_until = 0.0;
// prep.decelerate_after = 0.0;
prep.maximum_speed = prep.exit_speed;
}
}
}

// Initialize new segment
segment_t *prep_segment = &segment_buffer[segment_buffer_head];

// Set new segment to point to the current segment data block.
prep_segment->st_block_index = prep.st_block_index;

/*------------------------------------------------------------------------------------
Compute the average velocity of this new segment by determining the total distance
traveled over the segment time DT_SEGMENT. The following code first attempts to create
a full segment based on the current ramp conditions. If the segment time is incomplete
when terminating at a ramp state change, the code will continue to loop through the
progressing ramp states to fill the remaining segment execution time. However, if
an incomplete segment terminates at the end of the velocity profile, the segment is
considered completed despite having a truncated execution time less than DT_SEGMENT.
The velocity profile is always assumed to progress through the ramp sequence:
acceleration ramp, cruising state, and deceleration ramp. Each ramp's travel distance
may range from zero to the length of the block. Velocity profiles can end either at
the end of planner block (typical) or mid-block at the end of a forced deceleration,
such as from a feed hold.
*/
float dt_max = DT_SEGMENT; // Maximum segment time
float dt = 0.0; // Initialize segment time
float time_var = dt_max; // Time worker variable
float mm_var; // mm-Distance worker variable
float speed_var; // Speed worker variable
float mm_remaining = pl_block->millimeters; // New segment distance from end of block.
float minimum_mm = mm_remaining-prep.req_mm_increment; // Guarantee at least one step.
if (minimum_mm < 0.0) { minimum_mm = 0.0; }

do {
switch (prep.ramp_type) {
case RAMP_ACCEL:
// NOTE: Acceleration ramp only computes during first do-while loop.
speed_var = pl_block->acceleration*time_var;
mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var);
if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed);
if (mm_remaining == prep.decelerate_after) { prep.ramp_type = RAMP_DECEL; }
else { prep.ramp_type = RAMP_CRUISE; }
prep.current_speed = prep.maximum_speed;
} else { // Acceleration only.
prep.current_speed += speed_var;
}
break;
case RAMP_CRUISE:
// NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations.
// NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To
// prevent this, simply enforce a minimum speed threshold in the planner.
mm_var = mm_remaining - prep.maximum_speed*time_var;
if (mm_var < prep.decelerate_after) { // End of cruise.
// Cruise-deceleration junction or end of block.
time_var = (mm_remaining - prep.decelerate_after)/prep.maximum_speed;
mm_remaining = prep.decelerate_after; // NOTE: 0.0 at EOB
prep.ramp_type = RAMP_DECEL;
} else { // Cruising only.
mm_remaining = mm_var;
}
break;
default: // case RAMP_DECEL:
// NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed.
speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min)
if (prep.current_speed > speed_var) { // Check if at or below zero speed.
// Compute distance from end of segment to end of block.
mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm)
if (mm_var > prep.mm_complete) { // Deceleration only.
mm_remaining = mm_var;
prep.current_speed -= speed_var;
break; // Segment complete. Exit switch-case statement. Continue do-while loop.
}
} // End of block or end of forced-deceleration.
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
mm_remaining = prep.mm_complete;
}
dt += time_var; // Add computed ramp time to total segment time.
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
else {
if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps.
// Increase segment time to ensure at least one step in segment. Override and loop
// through distance calculations until minimum_mm or mm_complete.
dt_max += DT_SEGMENT;
time_var = dt_max - dt;
} else {
break; // **Complete** Exit loop. Segment execution time maxed.
}
}
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.

/* -----------------------------------------------------------------------------------
Compute segment step rate, steps to execute, and apply necessary rate corrections.
NOTE: Steps are computed by direct scalar conversion of the millimeter distance
remaining in the block, rather than incrementally tallying the steps executed per
segment. This helps in removing floating point round-off issues of several additions.
However, since floats have only 7.2 significant digits, long moves with extremely
high step counts can exceed the precision of floats, which can lead to lost steps.
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines
supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm).
*/
float steps_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps
float n_steps_remaining = ceil(steps_remaining); // Round-up current steps remaining
float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining
prep_segment->n_step = last_n_steps_remaining-n_steps_remaining; // Compute number of steps to execute.

// Bail if we are at the end of a feed hold and don't have a step to execute.
if (prep_segment->n_step == 0) {
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Less than one step to decelerate to zero speed, but already very close. AMASS
// requires full steps to execute. So, just bail.
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold.
prep.dt_remainder = 0.0;
prep.steps_remaining = n_steps_remaining;
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
plan_cycle_reinitialize();
return; // Segment not generated, but current step data still retained.
}
}

// Compute segment step rate. Since steps are integers and mm distances traveled are not,
// the end of every segment can have a partial step of varying magnitudes that are not
// executed, because the stepper ISR requires whole steps due to the AMASS algorithm. To
// compensate, we track the time to execute the previous segment's partial step and simply
// apply it with the partial step distance to the current segment, so that it minutely
// adjusts the whole segment rate to keep step output exact. These rate adjustments are
// typically very small and do not adversely effect performance, but ensures that Grbl
// outputs the exact acceleration and velocity profiles as computed by the planner.
dt += prep.dt_remainder; // Apply previous segment partial step execute time
float inv_rate = dt/(last_n_steps_remaining - steps_remaining); // Compute adjusted step rate inverse
prep.dt_remainder = (n_steps_remaining - steps_remaining)*inv_rate; // Update segment partial step time

// Compute CPU cycles per step for the prepped segment.
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)

#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// Compute step timing and multi-axis smoothing level.
// NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; }
else {
if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; }
else if (cycles < AMASS_LEVEL3) { prep_segment->amass_level = 2; }
else { prep_segment->amass_level = 3; }
cycles >>= prep_segment->amass_level;
prep_segment->n_step <<= prep_segment->amass_level;
}
if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; } // < 65536 (4.1ms @ 16MHz)
else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible.
#else
// Compute step timing and timer prescalar for normal step generation.
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
prep_segment->prescaler = 1; // prescaler: 0
prep_segment->cycles_per_tick = cycles;
} else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
prep_segment->prescaler = 2; // prescaler: 8
prep_segment->cycles_per_tick = cycles >> 3;
} else {
prep_segment->prescaler = 3; // prescaler: 64
if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
prep_segment->cycles_per_tick = cycles >> 6;
} else { // Just set the slowest speed possible. (Around 4 step/sec.)
prep_segment->cycles_per_tick = 0xffff;
}
}
#endif

// Segment complete! Increment segment buffer indices.
segment_buffer_head = segment_next_head;
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }

// Setup initial conditions for next segment.
if (mm_remaining > prep.mm_complete) {
// Normal operation. Block incomplete. Distance remaining in block to be executed.
pl_block->millimeters = mm_remaining;
prep.steps_remaining = steps_remaining;
} else {
// End of planner block or forced-termination. No more distance to be executed.
if (mm_remaining > 0.0) { // At end of forced-termination.
// Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete
// the segment queue, where realtime protocol will set new state upon receiving the
// cycle stop flag from the ISR. Prep_segment is blocked until then.
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold.
prep.dt_remainder = 0.0;
prep.steps_remaining = ceil(steps_remaining);
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
plan_cycle_reinitialize();
return; // Bail!
} else { // End of planner block
// The planner block is complete. All steps are set to be executed in the segment buffer.
pl_block = NULL; // Set pointer to indicate check and load next planner block.
plan_discard_current_block();
}
}

}
}

// Called by realtime status reporting to fetch the current speed being executed. This value
// however is not exactly the current speed, but the speed computed in the last step segment
// in the segment buffer. It will always be behind by up to the number of segment blocks (-1)
// divided by the ACCELERATION TICKS PER SECOND in seconds.
#ifdef REPORT_REALTIME_RATE
float st_get_realtime_rate()
{
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR)){
return prep.current_speed;
}
return 0.0f;
}
#endif

config.h

In here I disabled AMASS, it seem my 28BYJ-48 doesn’t move so well with AMASS enabled, and also it takes more space. I also disable variable spindle for more space. I don’t use any spindle anyway.

/*
config.h - compile time configuration
Part of Grbl

Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud

Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/

// This file contains compile-time configurations for Grbl's internal system. For the most part,
// users will not need to directly modify these, but they are here for specific needs, i.e.
// performance tuning or adjusting to non-typical machines.

// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them.

#ifndef config_h
#define config_h
#include "grbl.h" // For Arduino IDE compatibility.

// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
#define DEFAULTS_GENERIC

// Serial baud rate
#define BAUD_RATE 115200

// Default cpu mappings. Grbl officially supports the Arduino Uno only. Other processor types
// may exist from user-supplied templates or directly user-defined in cpu_map.h
#define CPU_MAP_ATMEGA328P // Arduino Uno CPU

// Define realtime command special characters. These characters are 'picked-off' directly from the
// serial read data stream and are not passed to the grbl line execution parser. Select characters
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
// g-code programs, maybe selected for interface programs.
// NOTE: If changed, manually update help message in report.c.
#define CMD_STATUS_REPORT '?'
#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
#define CMD_RESET 0x18 // ctrl-x.
#define CMD_SAFETY_DOOR '@'


// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
// mainly a safety feature to remind the user to home, since position is unknown to Grbl.
#define HOMING_INIT_LOCK // Comment to disable

// Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode
// to quickly engage the limit switches, followed by a slower locate mode, and finished by a short
// pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed
// in order starting with suffix 0 and completes the homing routine for the specified-axes only. If
// an axis is omitted from the defines, it will not home, nor will the system update its position.
// Meaning that this allows for users with non-standard cartesian machines, such as a lathe (x then z,
// with no y), to configure the homing cycle behavior to their needs.
// NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same
// cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing
// cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles.
// By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins
// may be reduced to one pin, if all axes are homed with seperate cycles, or vice versa, all three axes
// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits
// will not be affected by pin sharing.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // REQUIRED: First move Z to clear workspace.
#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // OPTIONAL: Then move X,Y at the same time.
// #define HOMING_CYCLE_2 // OPTIONAL: Uncomment and add axes mask to enable

// Number of homing cycles performed after when the machine initially jogs to limit switches.
// This help in preventing overshoot and should improve repeatability. This value should be one or
// greater.
#define N_HOMING_LOCATE_CYCLE 1 // Integer (1-128)

// After homing, Grbl will set by default the entire machine space into negative space, as is typical
// for professional CNC machines, regardless of where the limit switches are located. Uncomment this
// define to force Grbl to always set the machine origin at the homed location despite switch orientation.
// #define HOMING_FORCE_SET_ORIGIN // Uncomment to enable.

// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
// and addresses are defined in settings.h. With the current settings, up to 2 startup blocks may
// be stored and executed in order. These startup blocks would typically be used to set the g-code
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-2)

// Number of floating decimal points printed by Grbl for certain value types. These settings are
// determined by realistic and commonly observed values in CNC machines. For example, position
// values cannot be less than 0.001mm or 0.0001in, because machines can not be physically more
// precise this. So, there is likely no need to change these, but you can if you need to here.
// NOTE: Must be an integer value from 0 to ~4. More than 4 may exhibit round-off errors.
#define N_DECIMAL_COORDVALUE_INCH 4 // Coordinate or position value in inches
#define N_DECIMAL_COORDVALUE_MM 3 // Coordinate or position value in mm
#define N_DECIMAL_RATEVALUE_INCH 1 // Rate or velocity value in in/min
#define N_DECIMAL_RATEVALUE_MM 0 // Rate or velocity value in mm/min
#define N_DECIMAL_SETTINGVALUE 3 // Decimals for floating point setting values

// If your machine has two limits switches wired in parallel to one axis, you will need to enable
// this feature. Since the two switches are sharing a single pin, there is no way for Grbl to tell
// which one is enabled. This option only effects homing, where if a limit is engaged, Grbl will
// alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
// limit switch for each axis, don't enable this option. By keeping it disabled, you can perform a
// homing cycle while on the limit switch and not have to move the machine off of it.
// #define LIMITS_TWO_SWITCHES_ON_AXES

// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
// goes from 18 or 16 to make room for the additional line number data in the plan_block_t struct
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.

// Allows GRBL to report the real-time feed rate. Enabling this means that GRBL will be reporting more
// data with each status update.
// NOTE: This is experimental and doesn't quite work 100%. Maybe fixed or refactored later.
// #define REPORT_REALTIME_RATE // Disabled by default. Uncomment to enable.

// Upon a successful probe cycle, this option provides immediately feedback of the probe coordinates
// through an automatically generated message. If disabled, users can still access the last probe
// coordinates through Grbl '$#' print parameters.
#define MESSAGE_PROBE_COORDINATES // Enabled by default. Comment to disable.

// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
// analog pin 4. Only use this option if you require a second coolant control pin.
// NOTE: The M8 flood coolant control pin on analog pin 3 will still be functional regardless.
// #define ENABLE_M7 // Disabled by default. Uncomment to enable.

// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered,
// immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until
// the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the
// previous tool path, as if nothing happened.
// #define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable.

// After the safety door switch has been toggled and restored, this setting sets the power-up delay
// between restoring the spindle and coolant and resuming the cycle.
// NOTE: Delay value is defined in milliseconds from zero to 65,535.
#define SAFETY_DOOR_SPINDLE_DELAY 4000
#define SAFETY_DOOR_COOLANT_DELAY 1000

// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
// #define HOMING_CYCLE_0 (1<<X_AXIS) and #define HOMING_CYCLE_1 (1<<Y_AXIS)
// NOTE: This configuration option alters the motion of the X and Y axes to principle of operation
// defined at (http://corexy.com/theory.html). Motors are assumed to positioned and wired exactly as
// described, if not, motions may move in strange directions. Grbl requires the CoreXY A and B motors
// have the same steps per mm internally.
// #define COREXY // Default disabled. Uncomment to enable.

// Inverts pin logic of the control command pins. This essentially means when this option is enabled
// you can use normally-closed switches, rather than the default normally-open switches.
// NOTE: If you require individual control pins inverted, keep this macro disabled and simply alter
// the CONTROL_INVERT_MASK definition in cpu_map.h files.
// #define INVERT_ALL_CONTROL_PINS // Default disabled. Uncomment to enable.

// Inverts select limit pin states based on the following mask. This effects all limit pin functions,
// such as hard limits and homing. However, this is different from overall invert limits setting.
// This build option will invert only the limit pins defined here, and then the invert limits setting
// will be applied to all of them. This is useful when a user has a mixed set of limit pins with both
// normally-open(NO) and normally-closed(NC) switches installed on their machine.
// NOTE: PLEASE DO NOT USE THIS, unless you have a situation that needs it.
// #define INVERT_LIMIT_PIN_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)) // Default disabled. Uncomment to enable.

// Inverts the spindle enable pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards.
// NOTE: If VARIABLE_SPINDLE is enabled(default), this option has no effect as the PWM output and
// spindle enable are combined to one pin. If you need both this option and spindle speed PWM,
// uncomment the config option USE_SPINDLE_DIR_AS_ENABLE_PIN below.
// #define INVERT_SPINDLE_ENABLE_PIN // Default disabled. Uncomment to enable.

// Enable control pin states feedback in status reports. The data is presented as simple binary of
// the control pin port (0 (low) or 1(high)), masked to show only the input pins. Non-control pins on the
// port will always show a 0 value. See cpu_map.h for the pin bitmap. As with the limit pin reporting,
// we do not recommend keeping this option enabled. Try to only use this for setting up a new CNC.
// #define REPORT_CONTROL_PIN_STATE // Default disabled. Uncomment to enable.

// When Grbl powers-cycles or is hard reset with the Arduino reset button, Grbl boots up with no ALARM
// by default. This is to make it as simple as possible for new users to start using Grbl. When homing
// is enabled and a user has installed limit switches, Grbl will boot up in an ALARM state to indicate
// Grbl doesn't know its position and to force the user to home before proceeding. This option forces
// Grbl to always initialize into an ALARM state regardless of homing or not. This option is more for
// OEMs and LinuxCNC users that would like this power-cycle behavior.
// #define FORCE_INITIALIZATION_ALARM // Default disabled. Uncomment to enable.

// ---------------------------------------------------------------------------------------
// ADVANCED CONFIGURATION OPTIONS:

// Enables minimal reporting feedback mode for GUIs, where human-readable strings are not as important.
// This saves nearly 2KB of flash space and may allow enough space to install other/future features.
// GUIs will need to install a look-up table for the error-codes that Grbl sends back in their place.
// NOTE: This feature is new and experimental. Make sure the GUI you are using supports this mode.
// #define REPORT_GUI_MODE // Default disabled. Uncomment to enable.

// The temporal resolution of the acceleration management subsystem. A higher number gives smoother
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
// impact performance. The correct value for this parameter is machine dependent, so it's advised to
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
// NOTE: Changing this value also changes the execution time of a segment in the step segment buffer.
// When increasing this value, this stores less overall time in the segment buffer and vice versa. Make
// certain the step segment buffer is increased/decreased to account for these changes.
#define ACCELERATION_TICKS_PER_SECOND 100

// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
// smoothing the stepping of multi-axis motions. This feature smooths motion particularly at low step
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
// step smoothing. See stepper.c for more details on the AMASS system works.
//#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.

// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
// check in the settings module to prevent settings values that will exceed this limitation. The maximum
// step rate is strictly limited by the CPU speed and will change if something other than an AVR running
// at 16MHz is used.
// NOTE: For now disabled, will enable if flash space permits.
// #define MAX_STEP_RATE_HZ 30000 // Hz

// By default, Grbl sets all input pins to normal-high operation with their internal pull-up resistors
// enabled. This simplifies the wiring for users by requiring only a switch connected to ground,
// although its recommended that users take the extra step of wiring in low-pass filter to reduce
// electrical noise detected by the pin. If the user inverts the pin in Grbl settings, this just flips
// which high or low reading indicates an active signal. In normal operation, this means the user
// needs to connect a normal-open switch, but if inverted, this means the user should connect a
// normal-closed switch.
// The following options disable the internal pull-up resistors, sets the pins to a normal-low
// operation, and switches must be now connect to Vcc instead of ground. This also flips the meaning
// of the invert pin Grbl setting, where an inverted setting now means the user should connect a
// normal-open switch and vice versa.
// NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes.
// WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors!
//#define DISABLE_LIMIT_PIN_PULL_UP
//#define DISABLE_PROBE_PIN_PULL_UP
//#define DISABLE_CONTROL_PIN_PULL_UP

// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// the selected axis with the tool oriented toward the negative direction. In other words, a positive
// tool length offset value is subtracted from the current location.
#define TOOL_LENGTH_OFFSET_AXIS Z_AXIS // Default z-axis. Valid values are X_AXIS, Y_AXIS, or Z_AXIS.

// Enables variable spindle output voltage for different RPM values. On the Arduino Uno, the spindle
// enable pin will output 5V for maximum RPM with 256 intermediate levels and 0V when disabled.
// NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch!
// The hardware PWM output on pin D11 is required for variable spindle output voltages.
//#define VARIABLE_SPINDLE // Default enabled. Comment to disable.

// Used by the variable spindle output only. These parameters set the maximum and minimum spindle speed
// "S" g-code values to correspond to the maximum and minimum pin voltages. There are 256 discrete and
// equally divided voltage bins between the maximum and minimum spindle speeds. So for a 5V pin, 1000
// max rpm, and 250 min rpm, the spindle output voltage would be set for the following "S" commands:
// "S1000" @ 5V, "S250" @ 0.02V, and "S625" @ 2.5V (mid-range). The pin outputs 0V when disabled.
#define SPINDLE_MAX_RPM 1000.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM.
#define SPINDLE_MIN_RPM 0.0 // Min spindle RPM. This value is equal to (1/256) duty cycle on the PWM.

// Used by variable spindle output only. This forces the PWM output to a minimum duty cycle when enabled.
// When disabled, the PWM pin will still read 0V. Most users will not need this option, but it may be
// useful in certain scenarios. This setting does not update the minimum spindle RPM calculations. Any
// spindle RPM output lower than this value will be set to this value.
// #define MINIMUM_SPINDLE_PWM 5 // Default disabled. Uncomment to enable. Integer (0-255)

// By default on a 328p(Uno), Grbl combines the variable spindle PWM and the enable into one pin to help
// preserve I/O pins. For certain setups, these may need to be separate pins. This configure option uses
// the spindle direction pin(D13) as a separate spindle enable pin along with spindle speed PWM on pin D11.
// NOTE: This configure option only works with VARIABLE_SPINDLE enabled and a 328p processor (Uno).
// NOTE: With no direction pin, the spindle clockwise M4 g-code command will be removed. M3 and M5 still work.
// NOTE: BEWARE! The Arduino bootloader toggles the D13 pin when it powers up. If you flash Grbl with
// a programmer (you can use a spare Arduino as "Arduino as ISP". Search the web on how to wire this.),
// this D13 LED toggling should go away. We haven't tested this though. Please report how it goes!
// #define USE_SPINDLE_DIR_AS_ENABLE_PIN // Default disabled. Uncomment to enable.

// With this enabled, Grbl sends back an echo of the line it has received, which has been pre-parsed (spaces
// removed, capitalized letters, no comments) and is to be immediately executed by Grbl. Echoes will not be
// sent upon a line buffer overflow, but should for all normal lines sent to Grbl. For example, if a user
// sendss the line 'g1 x1.032 y2.45 (test comment)', Grbl will echo back in the form '[echo: G1X1.032Y2.45]'.
// NOTE: Only use this for debugging purposes!! When echoing, this takes up valuable resources and can effect
// performance. If absolutely needed for normal operation, the serial write buffer should be greatly increased
// to help minimize transmission waiting within the serial write protocol.
// #define REPORT_ECHO_LINE_RECEIVED // Default disabled. Uncomment to enable.

// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
// every buffer block junction, except for starting from rest and end of the buffer, which are always
// zero. This value controls how fast the machine moves through junctions with no regard for acceleration
// limits or angle between neighboring block line move directions. This is useful for machines that can't
// tolerate the tool dwelling for a split second, i.e. 3d printers or laser cutters. If used, this value
// should not be much greater than zero or to the minimum value necessary for the machine to work.
#define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min)

// Sets the minimum feed rate the planner will allow. Any value below it will be set to this minimum
// value. This also ensures that a planned motion always completes and accounts for any floating-point
// round-off errors. Although not recommended, a lower value than 1.0 mm/min will likely work in smaller
// machines, perhaps to 0.1mm/min, but your success may vary based on multiple factors.
#define MINIMUM_FEED_RATE 1.0 // (mm/min)

// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
// are issues with the accuracy of the arc generations, or increased if arc execution is getting
// bogged down by too many trig calculations.
#define N_ARC_CORRECTION 12 // Integer (1-255)

// The arc G2/3 g-code standard is problematic by definition. Radius-based arcs have horrible numerical
// errors when arc at semi-circles(pi) or full-circles(2*pi). Offset-based arcs are much more accurate
// but still have a problem when arcs are full-circles (2*pi). This define accounts for the floating
// point issues when offset-based arcs are commanded as full circles, but get interpreted as extremely
// small arcs with around machine epsilon (1.2e-7rad) due to numerical round-off and precision issues.
// This define value sets the machine epsilon cutoff to determine if the arc is a full-circle or not.
// NOTE: Be very careful when adjusting this value. It should always be greater than 1.2e-7 but not too
// much greater than this. The default setting should capture most, if not all, full arc error situations.
#define ARC_ANGULAR_TRAVEL_EPSILON 5E-7 // Float (radians)

// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
// run-time command executions, like status reports, since these are performed between each dwell
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)

// Creates a delay between the direction pin setting and corresponding step pulse by creating
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
// sets the direction pins, and does not immediately set the stepper pins, as it would in
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
// values for certain setups have ranged from 5 to 20us.
// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.

// The number of linear motions in the planner buffer to be planned at any give time. The vast
// majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra
// available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino
// begins to crash due to the lack of available RAM or if the CPU is having trouble keeping
// up with planning new incoming motions as they are executed.
// #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h.

// Governs the size of the intermediary step segment buffer between the step execution algorithm
// and the planner blocks. Each segment is set of steps executed at a constant velocity over a
// fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner
// block velocity profile is traced exactly. The size of this buffer governs how much step
// execution lead time there is for other Grbl processes have to compute and do their thing
// before having to come back and refill this buffer, currently at ~50msec of step moves.
// #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h.

// Line buffer size from the serial input stream to be executed. Also, governs the size of
// each of the startup blocks, as they are each stored as a string of this size. Make sure
// to account for the available EEPROM at the defined memory address in settings.h and for
// the number of desired startup blocks.
// NOTE: 80 characters is not a problem except for extreme cases, but the line buffer size
// can be too small and g-code blocks can get truncated. Officially, the g-code standards
// support up to 256 characters. In future versions, this default will be increased, when
// we know how much extra memory space we can re-invest into this.
// #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h

// Serial send and receive buffer size. The receive buffer is often used as another streaming
// buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming
// interfaces will character count and track each block send to each block response. So,
// increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable
// memory allows. The send buffer primarily handles messages in Grbl. Only increase if large
// messages are sent and Grbl begins to stall, waiting to send the rest of the message.
// NOTE: Buffer size values must be greater than zero and less than 256.
// #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 64

// Toggles XON/XOFF software flow control for serial communications. Not officially supported
// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware
// on these chips do not support XON/XOFF flow control characters and the intermediate buffer
// in the chips cause latency and overflow problems with standard terminal programs. However,
// using specifically-programmed UI's to manage this latency problem has been confirmed to work.
// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard
// terminal programs since their firmware correctly manage these XON/XOFF characters. In any
// case, please report any successes to grbl administrators!
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.

// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// electrical interference on the signal cables from external sources. It's recommended to first
// use shielded signal cables with their shielding connected to ground (old USB/computer cables
// work well and are cheap to find) and wire in a low-pass circuit into each limit pin.
// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.

// Force Grbl to check the state of the hard limit switches when the processor detects a pin
// change inside the hard limit ISR routine. By default, Grbl will trigger the hard limits
// alarm upon any pin change, since bouncing switches can cause a state check like this to
// misread the pin. When hard limits are triggered, they should be 100% reliable, which is the
// reason that this option is disabled by default. Only if your system/electronics can guarantee
// that the switches don't bounce, we recommend enabling this option. This will help prevent
// triggering a hard limit when the machine disengages from the switch.
// NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled.
// #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable.

// ---------------------------------------------------------------------------------------
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:

#ifndef HOMING_CYCLE_0
#error "Required HOMING_CYCLE_0 not defined."
#endif

#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(VARIABLE_SPINDLE)
#error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with VARIABLE_SPINDLE enabled"
#endif

#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(CPU_MAP_ATMEGA328P)
#error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with a 328p processor"
#endif

// ---------------------------------------------------------------------------------------

#endif

Okay, I think that’s all! I hope I don’t miss anything and it’s work for you. Thanks for reading!

Arduino cnc plotter from old printer

Tag

, , , , ,

Akhirnya ada waktu juga nyelesain project ini. Cnc plotter berbasis arduino, menggunakan 2 printer 🖨 bekas dan 1 CD-drive.

Motor stepper menggunakan 28byj-48. Motor stepper bawaan printer terlalu kasar dan kurang presisi, dan yang satu lagi motor dc forward backward tidak balance.

Aplikasi menggunakan gcontrol dan gcode.

Referensi sumber : instructable arduino mini cnc, arduino forum, stackoverflow, dll.

 

DIY Simple External USB Hardisk Auto Power OFF

Tag

, , , , , ,

Ada beberapa hardisk internal yang saya perlukan datanya dalam penggunaan yang cukup sering. Biasanya saya menyalakan PC dan saya gunakan LAN Networking / Jaringan untuk mengaksesnya. Namun saya berpikir karena saya hampir setiap hari melakukannya cara ini tidak efektif, dan juga boros listrik. Jika anda masih belum paham, PC anda CPU saja tanpa monitor bisa menghabiskan daya 100 Watt lebih. Bayangkan jika CPU anda hidup terus non-stop dari pagi sampai malam seperti kasus saya ini berapa penghematan listrik yang bisa didapatkan dalam sebulan.

Sekedar catatan saja, sebelumnya saya menggunakan PC (Celeron 3GHz) setiap hari, dengan CPU selalu on setiap hari 24×7, dan hanya auto sleep 5 menit setiap 6 jam, dengan menggunakan laptop (Core Duo 2GHz) saya bisa menghemat tagihan listrik kurang lebih seratus ribu per bulan. Jika menggunakan processor i3 atau lebih tinggi pastinya lebih boros lagi.

Bisa saja saya membeli usb external disk enclosure khusus atau usb external disk baru (plus hardisknya) yang support auto-off. Namun kenapa saya harus melakukannya? Saya harus merogoh kocek cukup dalam, sementara saya bisa membuatnya sendiri tanpa mengeluarkan uang sepeserpun karena kebetulan semuanya sudah ada. Internal hardisk, usb external cable converter, power adaptor, dan komponen lain yang diperlukan sudah ada.

Ide pertama memasang konektor tambahan pada internal SATA di dalam laptop sepertinya tidak bisa dilakukan, walau controllernya sendiri sebenarnya ada, namun tidak ada tempat untuk konektor tambahan. Maka opsi berikutnya adalah menggunakan usb cable, dan external power adaptor.

Requirement

  • Internal hardisk
  • usb to SATA/IDE cable converter
  • ATX Power Supply
  • some electronic components, R, C, Transistor, cables and such

Untuk project ini saya menggunakan hardisk internal laptop 2.5″ yang memang saya memiliki beberapa. USB cable converter, saya sendiri sudah memilikinya. USB cable ini memiliki adaptor power eksternal, namun untuk project ini saya menggunakan ATX Power Supply (untuk fitur power-off) yang juga sudah ada beberapa di tempat saya. Komponen lain seperti transistor, resistor, capasitor dan kabel yang saya ambil dari stok yang ada. Jadi, praktis saya tidak mengeluarkan uang sepeser pun, hanya memanfaatkan barang-barang yang ada.

Rangkaian berikut akan mengontrol on-off ATX Power Supply berdasarkan sinyal dari USB Power.

auto-off-controller

Untuk kestabilan sinyal anda bisa menambahkan resistor basis-ground jika dirasa perlu, namun saya sendiri tidak menggunakannya. Input sinyal diambil dari tegangan 5v USB laptop (dari port USB). Anda bisa membuat sendiri kabel usb untuk dihubungkan ke input berdasarkan kreativitas anda. Saya sendiri kebetulan menggunakan tambahan slot usb eksternal, dan mengambilnya dari situ. Dengan demikian jika terjadi masalah dengan kabel anda misal seperti short dsb hanya usb eksternal saja yang rusak, sedangkan usb laptop tetap aman.

Dengan rangkaian ini, usb eksternal hardisk akan otomatis mati saat laptop mati (power usb laptop mati). Atau alternatif lain kita bisa juga mematikannya secara manual dengan cukup melepas kabel sinyal ke usb setelah melakukan safe remove hardware.

ATX Power Supply di desain untuk bekerja dengan beban tertentu, jika beban terlalu kecil tegangan keluaran bisa menjadi tidak stabil, atau bisa juga karena kualitas PSU sudah berkurang, misalnya seperti capasitor yang sudah mulai kering. Untuk itu jika usb eksternal hardisk anda mungkin susah terdeteksi atau mungkin bunyi tik-tik-tik-tik tetapi normal saat menggunakan adaptor bawaan coba anda pasang beban lain yang cocok dalam artian daya tidak besar namun cukup untuk penstabil beban dan aman misalnya seperti cd-rom. Kasus ini terjadi pada saya, dan saya atasi dengan pemasangan sebuah cd-rom drive. Cd-rom tidak harus normal membaca, yang penting masih menyala.

Tambahan

Jika anda menggunakan windows Vista ke atas (7), safe remove hardware tidak mematikan usb external hardisk anda (begitu juga dengan usb flashdisk, jika memiliki led indicator akan terlihat led masih tetap menyala). Ini bisa berbahaya bagi usb hardisk anda saat nanti dimatikan (power-off). Akan terdengar suara “cetek” yang cukup keras, menandakan hardisk sebenarnya masih berputar (spin-up) dan head tidak terparkir di landing zone. Beberapa mengatakan ini suara saat head melakukan emergency parking. Tergantung keberuntungan anda, hal ini bisa menyebabkan data rusak atau lebih parah lagi hardisk tidak bisa dipakai.

Untuk itu anda bisa mengubah fungsi safe remove hardware agar mematikan usb eksternal hardisk anda, atau alternatif lain bisa juga menggunakan aplikasi tambahan.

  • Windows Registry

Untuk mengaktifkan fitur ini, ubah setingan registry berikut:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\usbhub\HubG,
new item DisableOnSoftRemove (DWORD), ubah data value 1.

  • Aplikasi Tambahan

Untuk aplikasi tambahan yang saya coba adalah Safely Remove. Aplikasi ini tidak gratis, namun aplikasi ini memilliki opsi untuk mematikan device setelah safely remove/eject. Anda bisa melihat menu help pada aplikasi untuk lebih jelas.

http://safelyremove.com

Sebelum mematikan usb eksternal hardisk, pastikan selalu melakukan safe remove terlebih dahulu. Hal ini juga berlaku saat shutdown windows, karena OS yang saya pakai (Windows 7) sepertinya tidak mematikan hardisk eksternal saya (spin-down / parking) saat shutdown, sehingga ada suara cetek jika sebelum shutdown tidak di safe remove hardware terlebih dahulu.

Auto Safe Remove on Shutdown

Untuk dapat secara otomatis melakukan safe remove hardware kita akan membuat script berbasis console/command.

Anda bisa menggunakan aplikasi safely remove diatas untuk digunakan pada command prompt, namun saya memilih menggunakan removedrive dengan alasan aplikasi ini free alias gratis.

Dalam hal ini, saya menggunakan script saat logoff, dengan pertimbangan lebih banyak waktu bagi script untuk berjalan sehingga mengurangi kemungkinan script gagal dieksekusi karena mungkin waktu tidak cukup dan system keburu shutdown.

Jika script tidak berjalan saat sleep/hibernate mungkin bisa ditambahkan di event task scheduler. Dan script ini ada kemungkinan tidak dieksekusi bagi pengguna fastboot/fast shutdown di Windows 8 keatas.

  • Logoff script menggunakan gpedit

Hardisk eksternal saya terdeteksi sebagai Disk1, untuk itu kita bisa menggunakan parameter “Hardisk1” pada aplikasi removedrive.

disk1-managemet

Selanjutnya kita gunakan Group Policy Editor untuk menambahkan Logoff script.

Start -> Run dan ketik “gpedit.msc”

Tambahkan script pada User Configuration -> Windows Settings -> Scripts (Logon/Logoff)

removedrive \Device\Harddisk1

add-logoff-script

Sesuai gambar diatas, saya copy file removedrive.exe ke direktori windows.

* Download removedrive http://www.uwe-sieber.de/drivetools_e.html

Anda mungkin bisa memasang script ini dalam kondisi lain misalnya saat sleep atau hibernate jika diperlukan. Untuk kasus saya, hardisk pada posisi off saat sleep jadi aman dimatikan.

Demikian, semoga bermanfaat!

Referensi:

Datasheet IC CDI MB4213

Tag

, , , , , ,

datasheet schematic skema download IC CDI Motorcyle KGD 4-Tak 4-Stroke Supra Grand Karisma Shogun Smash unlimit limiter rpm advance timing

Kabar gembira buat kalian para pecinta modifikasi otomotif, setelah bertahun-tahun dari postingan pertama saya tentang CDI ini akhirnya sekarang sudah ada datasheet dari IC CDI ini.

Anda bahkan dapat membelinya sekarang, saya lihat ada yang jual di tokopedia seharga 25 ribu rupiah.

Dari datasheet diatas, bisa kita lihat IC MB4213 atau LD4213 ini memiliki pengatur RPM dan ADVANCE TIMING. Anda bisa berkseperimen dan mengubahnya sesuai keinginan.

Untuk lebih jelasnya bisa langsung ke pabriknya http://www.huaaoe.com/en/pd.jsp?id=2175

Modifikasi UPS 600 Kenika 3

Tag

, , ,

Prev – Modifikasi UPS 600 Kenika 2

Histeresys

Walau desain diatas bekerja cukup baik, dengan tegangan penuh 13.2V, aki tidak panas walau sudah beberapa minggu terhubung selalu. Namun untuk aki yang satunya, walaupun tegangan penuh diturunkan di sekitar 12V dan aki tidak panas, gelembung yang terjadi cukup deras dan mengakibatkan air aki cepat habis. Karena itu saya pikir untuk lebih benar-benar memutus arus pengisian.

Overvoltage konvensional, hanya mendeteksi tegangan penuh aki lalu saat tegangan penuh tercapai, koneksi ke aki diputus. Switch pemutus bisa menggunakan transistor atau relay. Konsep ini memiliki masalah, karena saat aki diputus, tegangan aki akan anjlok, otomatis rangkaian akan langsung aktif kembali lalu tegangan penuh kembali dan akan putus lagi dan terjadi looping seperti ini terus. Untuk mengatasi hal ini digunakan histeresys, dengan histeresys menggunakan dua masukan tegangan yang berbeda saat on dan off.

Desain histeresys sederhana ini memiliki keterbatasan rentang tegangan yang tidak bisa diatur sesuka hati, namun untuk desain charger ini saya pikir masih bisa diterima.

Overvoltage menggunakan histeresis sederhana mengacu standar rangkaian Schmitt Trigger menggunakan 3 buah transistor.

Histeresys 2

Saya membuat dua opsi pemasangan led. TP (Test Point) digunakan untuk pengetesan simulasi, setelah perangkaian TP disambung.

Schimtt Trigger sim

Dapat dilihat, tegangan off kurang lebih sekitar 13.2V dan tegangan on sekitar 11.5V. Untuk aki yang saya pakai, tegangan ini bisa diterima. Jika anda menggunakan aki lead-acid (asam sulfat), ada baiknya tegangan ini ditinggikan. Rangkaian ini saya desain demikian, saya ambil harga komponen terdekat yang ada, dan juga cukup dengan mengganti R4 dengan 1K jika saya ingin menggunakan aki yang satu lagi yang memiliki tegangan lebih rendah. Atau bisa juga diganti VR yang sesuai.

Rangkaian berikutnya mencoba untuk lebih menyederhanakan lagi, dengan dua buah transistor.

Histeresys 2

Hasil simulasi tidak secantik desain sebelumnya 😀 namun masih tetap bisa bekerja tentunya. Tegangan off sekitar 13.2V dan tegangan on sekitar 12V. Untuk aki yang satunya, cukup mengganti/mengubah VR3 menjadi 1.2K.

Histeresys 2 sim

Tentu saja tegangan ini disesuaikan dengan aki yang anda pakai. Dan hasil akhirnya, mungkin akan sedikit berbeda dengan desain, dan menyesuaikan dengan komponen yang ada didapatkan Tegangan Charging Off (Led menyala) pada ~13.3V dan Tegangan Charging On (Led Mati) pada ~12.3V, saya pikir cukup bagus untuk aki yang saya pakai.

DSC05855

Saat tegangan mencapai ~13.3V led merah (green di skematik) menyala, charger terputus (atau lebih tepatnya turun menjadi sekitar 6V), tegangan aki akan langsung turun (anjlok) di sekitar 12.7V dan terus turun sampai tegangan ON tercapai di sekitaran 12.3V, led akan padam dan pengisian kembali dilakukan sampai tegangan aki kembali ke sekitar ~13.3V dan demikian seterusnya. Setelah semuanya dirasa berfungsi dengan baik, saatnya untuk merapikan rangkaian.

Dalam project ini, desain teori sepertinya tidak selamanya sesuai dengan realita, dan ada kalanya kita harus mengubah desain untuk mengikuti keadaan komponen yang ada. Nothing’s perfect.

Referensi:

* https://en.wikipedia.org/wiki/Schmitt_trigger