Port stepper
This commit is contained in:
parent
fdf6e31218
commit
d4cbac2a5c
@ -415,14 +415,18 @@
|
||||
// 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.
|
||||
// not ported; don't use. #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
|
||||
|
||||
// Creates a delay between setting the direction pin and pulsing the step pin. This delay
|
||||
// lives inside the main Grbl interrupt.
|
||||
#define STEP_PULSE_DELAY_NS 200 // Step pulse delay in nanoseconds.
|
||||
|
||||
// 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 Mega2560. 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 16 // Uncomment to override default in planner.h.
|
||||
#define BLOCK_BUFFER_SIZE 32 // 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
|
||||
@ -617,9 +621,9 @@
|
||||
#define Y_LIMIT_BIT 27 // Y-MIN=26, Y-MAX=27
|
||||
#define Z_LIMIT_BIT 29 // Z-MIN=28, Z-MAX=29
|
||||
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
|
||||
// hard/soft limit not ported #define LIMIT_INT PCIE0 // Pin change interrupt enable pin
|
||||
// hard/soft limit not ported #define LIMIT_INT_vect PCINT0_vect
|
||||
// hard/soft limit not ported #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
|
||||
// hard limits not ported #define LIMIT_INT PCIE0 // Pin change interrupt enable pin
|
||||
// hard limits not ported #define LIMIT_INT_vect PCINT0_vect
|
||||
// hard limits not ported #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
|
||||
|
||||
// Define spindle enable and spindle direction output pins.
|
||||
#define SPINDLE_ENABLE_DDR DDRB
|
||||
|
@ -73,7 +73,7 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
|
||||
// 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
|
||||
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
||||
uint32_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
||||
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
|
||||
@ -97,7 +97,8 @@ typedef struct {
|
||||
#endif
|
||||
|
||||
uint8_t execute_step; // Flags step execution for each interrupt.
|
||||
uint8_t step_pulse_time; // Step pulse reset time after step rise
|
||||
uint32_t step_setup_time; // Delay between dir change and step pulse
|
||||
uint32_t step_pulse_time; // Step pulse width
|
||||
uint32_t step_outbits; // The next stepping-bits to be output
|
||||
uint32_t dir_outbits;
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
@ -220,12 +221,14 @@ void st_wake_up()
|
||||
// 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);
|
||||
st.step_setup_time = uint32_t(uint64_t(SystemCoreClock)*STEP_PULSE_DELAY_NS/1000'000'000);
|
||||
st.step_pulse_time = settings.pulse_microseconds*TICKS_PER_MICROSECOND;
|
||||
#endif
|
||||
|
||||
// Enable Stepper Driver Interrupt
|
||||
TIMSK1 |= (1<<OCIE1A);
|
||||
// Enable Stepper Driver Interrupt Timer
|
||||
LPC_TIM1->TCR = 0b10; // reset
|
||||
LPC_TIM1->MR0 = 4000; // Generate first interrupt soon
|
||||
LPC_TIM1->TCR = 0b01; // enable
|
||||
}
|
||||
|
||||
|
||||
@ -233,8 +236,7 @@ void st_wake_up()
|
||||
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.
|
||||
LPC_TIM1->TCR = 0; // Disable Timer1
|
||||
busy = false;
|
||||
|
||||
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
|
||||
@ -299,8 +301,9 @@ void st_go_idle()
|
||||
// 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)
|
||||
extern "C" void TIMER1_IRQHandler()
|
||||
{
|
||||
LPC_TIM1->IR = LPC_TIM1->IR; // Clear interrupt
|
||||
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
|
||||
@ -308,19 +311,26 @@ ISR(TIMER1_COMPA_vect)
|
||||
|
||||
// Then pulse the stepping pins
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
#error not implemented
|
||||
st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
|
||||
#else // Normal operation
|
||||
delay_loop(get_time(), st.step_setup_time);
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
|
||||
// Mark time step bits were set
|
||||
uint32_t step_start_time = get_time();
|
||||
#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
|
||||
// Disabled. This ISR now resets the step pins at the end.
|
||||
// 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.
|
||||
|
||||
// Can no longer re-enable interrupts since this ISR resets the step pins at the end.
|
||||
// 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) {
|
||||
@ -330,12 +340,13 @@ ISR(TIMER1_COMPA_vect)
|
||||
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
||||
|
||||
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
#error not ported; AMASS is required
|
||||
// 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;
|
||||
LPC_TIM1->MR0 = 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.
|
||||
@ -368,6 +379,9 @@ ISR(TIMER1_COMPA_vect)
|
||||
if (st.exec_block->is_pwm_rate_adjusted) { spindle_set_speed(SPINDLE_PWM_OFF_VALUE); }
|
||||
#endif
|
||||
system_set_exec_state_flag(EXEC_CYCLE_STOP); // Flag main program for cycle end
|
||||
// Reset stepping pins after delay
|
||||
delay_loop(step_start_time, st.step_pulse_time);
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
|
||||
return; // Nothing to do but exit.
|
||||
}
|
||||
}
|
||||
@ -425,6 +439,11 @@ ISR(TIMER1_COMPA_vect)
|
||||
}
|
||||
|
||||
st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask
|
||||
|
||||
// Reset stepping pins after delay
|
||||
delay_loop(step_start_time, st.step_pulse_time);
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
|
||||
|
||||
busy = false;
|
||||
}
|
||||
|
||||
@ -437,6 +456,8 @@ ISR(TIMER1_COMPA_vect)
|
||||
cause issues at high step rates if another high frequency asynchronous interrupt is
|
||||
added to Grbl.
|
||||
*/
|
||||
/* Not ported; the main ISR assumes short pulse widths and handles it. Stepper drivers with
|
||||
long pulse widths may need this instead if they cause the main ISR to consume too much time.
|
||||
// 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.
|
||||
@ -457,6 +478,7 @@ ISR(TIMER0_OVF_vect)
|
||||
STEP_PORT = st.step_bits; // Begin step pulse.
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
|
||||
// Generates the step and direction port invert masks used in the Stepper Interrupt Driver.
|
||||
@ -506,21 +528,13 @@ void stepper_init()
|
||||
DIRECTION_DDR |= DIRECTION_MASK;
|
||||
|
||||
// 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
|
||||
LPC_TIM1->TCR = 0; // disable
|
||||
LPC_TIM1->CTCR = 0; // timer mode
|
||||
LPC_TIM1->PR = 0; // no prescale
|
||||
LPC_TIM1->MCR = 0b011; // MR0: !stop, reset, interrupt
|
||||
LPC_TIM1->CCR = 0; // no capture
|
||||
LPC_TIM1->EMR = 0; // no external match
|
||||
NVIC_EnableIRQ(TIMER1_IRQn); // Enable Stepper Driver Interrupt
|
||||
}
|
||||
|
||||
|
||||
@ -948,9 +962,9 @@ void st_prep_buffer()
|
||||
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.
|
||||
prep_segment->cycles_per_tick = cycles;
|
||||
#else
|
||||
#error not ported; AMASS is required
|
||||
// Compute step timing and timer prescalar for normal step generation.
|
||||
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
|
||||
prep_segment->prescaler = 1; // prescaler: 0
|
||||
|
@ -28,28 +28,14 @@ extern DummyReg EECR;
|
||||
extern DummyReg EEDR;
|
||||
extern DummyReg EEMPE;
|
||||
extern DummyReg EEPE;
|
||||
extern DummyReg OCIE0A;
|
||||
extern DummyReg OCIE0B;
|
||||
extern DummyReg OCIE1A;
|
||||
extern DummyReg OCR1A;
|
||||
extern DummyReg OCR2A;
|
||||
extern DummyReg PCICR;
|
||||
extern DummyReg PCIE0;
|
||||
extern DummyReg PCIE1;
|
||||
extern DummyReg PCMSK0;
|
||||
extern DummyReg PCMSK1;
|
||||
extern DummyReg SREG;
|
||||
extern DummyReg TCCR0A;
|
||||
extern DummyReg TCCR0B;
|
||||
extern DummyReg TCCR1A;
|
||||
extern DummyReg TCCR1B;
|
||||
extern DummyReg TCCRA;
|
||||
extern DummyReg TCCR2A;
|
||||
extern DummyReg TCCR2B;
|
||||
extern DummyReg TCNT0;
|
||||
extern DummyReg TIMSK0;
|
||||
extern DummyReg TIMSK1;
|
||||
extern DummyReg TOIE0;
|
||||
|
||||
extern DummyReg UCSR0A;
|
||||
extern DummyReg UCSR0B;
|
||||
@ -68,10 +54,6 @@ static const int COM2A1 = 0;
|
||||
static const int COM2B0 = 0;
|
||||
static const int COM2B1 = 0;
|
||||
|
||||
static const int CS01 = 0;
|
||||
static const int CS10 = 0;
|
||||
static const int CS11 = 0;
|
||||
static const int CS12 = 0;
|
||||
static const int CS22 = 0;
|
||||
static const int EEMWE = 0;
|
||||
static const int EERE = 0;
|
||||
|
@ -24,15 +24,25 @@
|
||||
|
||||
void delay_init();
|
||||
|
||||
// Get current time in clock cycles
|
||||
inline uint32_t get_time()
|
||||
{
|
||||
return LPC_TIM3->TC;
|
||||
}
|
||||
|
||||
// Wait until get_time() is numCycles past startTime. Handles timer wrap.
|
||||
inline void delay_loop(uint32_t startTime, uint32_t numCycles)
|
||||
{
|
||||
while (get_time() - startTime < numCycles)
|
||||
;
|
||||
}
|
||||
|
||||
inline void delay_us(uint32_t us)
|
||||
{
|
||||
uint32_t start = LPC_TIM3->TC;
|
||||
uint32_t cycles = uint32_t(uint64_t(SystemCoreClock) * us / 1000000);
|
||||
while (LPC_TIM3->TC - start < cycles)
|
||||
;
|
||||
delay_loop(get_time(), uint32_t(uint64_t(SystemCoreClock) * us / 1'000'000));
|
||||
}
|
||||
|
||||
inline void delay_ms(uint32_t ms)
|
||||
{
|
||||
return delay_us(ms * 1000);
|
||||
delay_loop(get_time(), uint32_t(uint64_t(SystemCoreClock) * ms / 1000));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user