mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-08-29 17:19:50 +02:00
Cleaned up AMASS code (#635)
* Cleaned up AMASS code More #defines gone 74 lines shorter Tested by comparing the result of original AMASS computation code to the new code with values surrounding all of the cutoff frequencies. * I2SOut tick calculation * Sorted out units for stepper pulse periods I tried to make it clear what the units are at different places in the code, and to use argument datatypes that clearly show the value range at different points, instead of relying on implicit type promotion. Hopefully this will make it easier to understand when, where, and why unit conversions occur. * Update Stepper.h * Deleted AMASS Config.h option ... as it is no longer optional
This commit is contained in:
@@ -369,13 +369,6 @@ const int REPORT_WCO_REFRESH_IDLE_COUNT = 10; // (2-255) Must be less than or e
|
||||
// certain the step segment buffer is increased/decreased to account for these changes.
|
||||
const int 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
|
||||
|
@@ -116,8 +116,8 @@ static portMUX_TYPE i2s_out_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static int i2s_out_initialized = 0;
|
||||
|
||||
# ifdef USE_I2S_OUT_STREAM_IMPL
|
||||
static volatile uint64_t i2s_out_pulse_period;
|
||||
static uint64_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
|
||||
static volatile uint32_t i2s_out_pulse_period;
|
||||
static uint32_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
|
||||
static volatile i2s_out_pulse_func_t i2s_out_pulse_func;
|
||||
# endif
|
||||
|
||||
@@ -657,10 +657,9 @@ int IRAM_ATTR i2s_out_set_stepping() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IRAM_ATTR i2s_out_set_pulse_period(uint64_t period) {
|
||||
int IRAM_ATTR i2s_out_set_pulse_period(uint32_t period) {
|
||||
# ifdef USE_I2S_OUT_STREAM_IMPL
|
||||
// Use 64-bit values to avoid overflowing during the calculation.
|
||||
i2s_out_pulse_period = period * 1000000 / F_STEPPER_TIMER;
|
||||
i2s_out_pulse_period = period;
|
||||
# endif
|
||||
return 0;
|
||||
}
|
||||
|
@@ -156,10 +156,9 @@ int i2s_out_set_stepping();
|
||||
void i2s_out_delay();
|
||||
|
||||
/*
|
||||
Set the pulse callback period in ISR ticks.
|
||||
(same value of the timer period for the ISR)
|
||||
Set the pulse callback period in microseconds
|
||||
*/
|
||||
int i2s_out_set_pulse_period(uint64_t period);
|
||||
int i2s_out_set_pulse_period(uint32_t usec);
|
||||
|
||||
/*
|
||||
Register a callback function to generate pulse data
|
||||
|
@@ -15,8 +15,7 @@
|
||||
#endif
|
||||
|
||||
// ESP32 CPU Settings
|
||||
const int32_t F_TIMERS = 80000000; // a reference to the speed of ESP32 timers
|
||||
#define F_STEPPER_TIMER 20000000 // frequency of step pulse timer
|
||||
const uint32_t fTimers = 80000000; // a reference to the speed of ESP32 timers
|
||||
|
||||
// =============== Don't change or comment these out ======================
|
||||
// They are for legacy purposes and will not affect your I/O
|
||||
|
@@ -61,7 +61,6 @@ static inline int toMotor2(int axis) {
|
||||
// Conversions
|
||||
const double MM_PER_INCH = (25.40);
|
||||
const double INCH_PER_MM = (0.0393701);
|
||||
#define TICKS_PER_MICROSECOND (F_STEPPER_TIMER / 1000000) // Different from AVR version
|
||||
|
||||
const int DELAY_MODE_DWELL = 0;
|
||||
const int DELAY_MODE_SYS_SUSPEND = 1;
|
||||
|
@@ -45,14 +45,10 @@ 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.
|
||||
uint16_t isrPeriod; // Time to next ISR tick, in units of timer ticks
|
||||
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
|
||||
#else
|
||||
uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
|
||||
#endif
|
||||
uint16_t spindle_rpm; // TODO get rid of this.
|
||||
uint8_t amass_level; // AMASS level for the ISR to execute this segment
|
||||
uint16_t spindle_rpm; // TODO get rid of this.
|
||||
} segment_t;
|
||||
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
||||
|
||||
@@ -70,9 +66,7 @@ typedef struct {
|
||||
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[MAX_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.
|
||||
@@ -94,7 +88,7 @@ static volatile uint8_t busy;
|
||||
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
|
||||
|
||||
// esp32 work around for diable in main loop
|
||||
// esp32 work around for disable in main loop
|
||||
uint64_t stepper_idle_counter; // used to count down until time to disable stepper drivers
|
||||
bool stepper_idle;
|
||||
|
||||
@@ -243,7 +237,7 @@ static void stepper_pulse_func() {
|
||||
// Initialize new step segment and load number of steps to execute
|
||||
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
||||
// Initialize step segment timing per step and load number of steps to execute.
|
||||
Stepper_Timer_WritePeriod(st.exec_segment->cycles_per_tick);
|
||||
Stepper_Timer_WritePeriod(st.exec_segment->isrPeriod);
|
||||
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.
|
||||
@@ -258,12 +252,10 @@ static void stepper_pulse_func() {
|
||||
// TODO ABC
|
||||
}
|
||||
st.dir_outbits = st.exec_block->direction_bits;
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
// With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
|
||||
// Adjust Bresenham axis increment counters according to AMASS level.
|
||||
for (int axis = 0; axis < n_axis; axis++) {
|
||||
st.steps[axis] = st.exec_block->steps[axis] >> st.exec_segment->amass_level;
|
||||
}
|
||||
#endif
|
||||
// Set real-time spindle output as segment is loaded, just prior to the first step.
|
||||
spindle->set_rpm(st.exec_segment->spindle_rpm);
|
||||
} else {
|
||||
@@ -374,7 +366,7 @@ void st_wake_up() {
|
||||
// Step pulse delay handling is not require with ESP32...the RMT function does it.
|
||||
#else // Normal operation
|
||||
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
|
||||
st.step_pulse_time = -(((pulse_microseconds->get() - 2) * TICKS_PER_MICROSECOND) >> 3);
|
||||
st.step_pulse_time = -(((pulse_microseconds->get() - 2) * ticksPerMicrosecond) >> 3);
|
||||
#endif
|
||||
// Enable Stepper Driver Interrupt
|
||||
Stepper_Timer_Start();
|
||||
@@ -538,20 +530,15 @@ void st_prep_buffer() {
|
||||
st_prep_block->direction_bits = pl_block->direction_bits;
|
||||
uint8_t idx;
|
||||
auto n_axis = number_axis->get();
|
||||
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
for (idx = 0; idx < n_axis; idx++) {
|
||||
st_prep_block->steps[idx] = pl_block->steps[idx];
|
||||
}
|
||||
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.
|
||||
|
||||
// Bit-shift multiply all Bresenham data by the max AMASS level so 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.
|
||||
for (idx = 0; idx < n_axis; idx++) {
|
||||
st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->steps[idx] = pl_block->steps[idx] << maxAmassLevel;
|
||||
}
|
||||
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
|
||||
#endif
|
||||
st_prep_block->step_event_count = pl_block->step_event_count << maxAmassLevel;
|
||||
|
||||
// Initialize segment buffer data for generating the segments.
|
||||
prep.steps_remaining = (float)pl_block->step_event_count;
|
||||
prep.step_per_mm = prep.steps_remaining / pl_block->millimeters;
|
||||
@@ -839,49 +826,28 @@ void st_prep_buffer() {
|
||||
// outputs the exact acceleration and velocity profiles as computed by the planner.
|
||||
|
||||
dt += prep.dt_remainder; // Apply previous segment partial step execute time
|
||||
// dt is in minutes so inv_rate is in minutes
|
||||
float inv_rate = dt / (last_n_steps_remaining - step_dist_remaining); // Compute adjusted step rate inverse
|
||||
|
||||
// Compute CPU cycles per step for the prepped segment.
|
||||
uint32_t cycles = ceil((TICKS_PER_MICROSECOND * 1000000 * 60) * inv_rate); // (cycles/step)
|
||||
// fStepperTimer is in units of timerTicks/sec, so the dimensional analysis is
|
||||
// timerTicks/sec * 60 sec/minute * minutes = timerTicks
|
||||
uint32_t timerTicks = ceil((fStepperTimer * 60) * inv_rate); // (timerTicks/step)
|
||||
int level;
|
||||
|
||||
#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;
|
||||
for (level = 0; level < maxAmassLevel; level++) {
|
||||
if (timerTicks < amassThreshold) {
|
||||
break;
|
||||
}
|
||||
cycles >>= prep_segment->amass_level;
|
||||
prep_segment->n_step <<= prep_segment->amass_level;
|
||||
timerTicks >>= 1;
|
||||
}
|
||||
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
|
||||
prep_segment->amass_level = level;
|
||||
prep_segment->n_step <<= level;
|
||||
// isrPeriod is stored as 16 bits, so limit timerTicks to the
|
||||
// largest value that will fit in a uint16_t.
|
||||
prep_segment->isrPeriod = timerTicks > 0xffff ? 0xffff : timerTicks;
|
||||
|
||||
// Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it.
|
||||
segment_buffer_head = segment_next_head;
|
||||
if (++segment_next_head == SEGMENT_BUFFER_SIZE) {
|
||||
@@ -935,21 +901,23 @@ float st_get_realtime_rate() {
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR Stepper_Timer_WritePeriod(uint64_t alarm_val) {
|
||||
// The argument is in units of ticks of the timer that generates ISRs
|
||||
void IRAM_ATTR Stepper_Timer_WritePeriod(uint16_t timerTicks) {
|
||||
if (current_stepper == ST_I2S_STREAM) {
|
||||
#ifdef USE_I2S_STEPS
|
||||
// 1 tick = F_TIMERS / F_STEPPER_TIMER
|
||||
// 1 tick = fTimers / fStepperTimer
|
||||
// Pulse ISR is called for each tick of alarm_val.
|
||||
i2s_out_set_pulse_period(alarm_val);
|
||||
// The argument to i2s_out_set_pulse_period is in units of microseconds
|
||||
i2s_out_set_pulse_period(((uint32_t)timerTicks) / ticksPerMicrosecond);
|
||||
#endif
|
||||
} else {
|
||||
timer_set_alarm_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, alarm_val);
|
||||
timer_set_alarm_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, (uint64_t)timerTicks);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR Stepper_Timer_Init() {
|
||||
timer_config_t config;
|
||||
config.divider = F_TIMERS / F_STEPPER_TIMER;
|
||||
config.divider = fTimers / fStepperTimer;
|
||||
config.counter_dir = TIMER_COUNT_UP;
|
||||
config.counter_en = TIMER_PAUSE;
|
||||
config.alarm_en = TIMER_ALARM_EN;
|
||||
|
@@ -46,25 +46,23 @@ struct PrepFlag {
|
||||
uint8_t decelOverride : 1;
|
||||
};
|
||||
|
||||
// fStepperTimer should be an integer divisor of the bus speed, i.e. of fTimers
|
||||
const uint32_t fStepperTimer = 20000000; // frequency of step pulse timer
|
||||
const int ticksPerMicrosecond = fStepperTimer / 1000000;
|
||||
|
||||
// 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).
|
||||
// For efficient computation, each cutoff frequency is twice the previous one.
|
||||
// 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.
|
||||
///#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
#define MAX_AMASS_LEVEL 3
|
||||
// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
|
||||
// Note ESP32 use F_STEPPER_TIMER rather than the AVR F_CPU
|
||||
const int AMASS_LEVEL1 = (F_STEPPER_TIMER / 8000); // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
|
||||
const int AMASS_LEVEL2 = (F_STEPPER_TIMER / 4000); // Over-drives ISR (x4)
|
||||
const int AMASS_LEVEL3 = (F_STEPPER_TIMER / 2000); // Over-drives ISR (x8)
|
||||
|
||||
static_assert(MAX_AMASS_LEVEL >= 1, "AMASS must have 1 or more levels to operate correctly.");
|
||||
//#endif
|
||||
const uint32_t amassThreshold = fStepperTimer / 8000;
|
||||
const int maxAmassLevel = 3; // Each level increase doubles the threshold
|
||||
|
||||
const timer_group_t STEP_TIMER_GROUP = TIMER_GROUP_0;
|
||||
const timer_idx_t STEP_TIMER_INDEX = TIMER_0;
|
||||
@@ -132,7 +130,7 @@ bool get_stepper_disable(); // returns the state of the pin
|
||||
void set_stepper_pins_on(uint8_t onMask);
|
||||
void set_direction_pins_on(uint8_t onMask);
|
||||
|
||||
void Stepper_Timer_WritePeriod(uint64_t alarm_val);
|
||||
void Stepper_Timer_WritePeriod(uint16_t timerTicks);
|
||||
void Stepper_Timer_Init();
|
||||
void Stepper_Timer_Start();
|
||||
void Stepper_Timer_Stop();
|
||||
|
Reference in New Issue
Block a user