Skip to content

Commit ab36c5a

Browse files
authored
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
1 parent 01aa028 commit ab36c5a

File tree

7 files changed

+50
-95
lines changed

7 files changed

+50
-95
lines changed

Grbl_Esp32/src/Config.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,6 @@ const int REPORT_WCO_REFRESH_IDLE_COUNT = 10; // (2-255) Must be less than or e
369369
// certain the step segment buffer is increased/decreased to account for these changes.
370370
const int ACCELERATION_TICKS_PER_SECOND = 100;
371371

372-
// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
373-
// smoothing the stepping of multi-axis motions. This feature smooths motion particularly at low step
374-
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
375-
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
376-
// step smoothing. See Stepper.c for more details on the AMASS system works.
377-
#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.
378-
379372
// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
380373
// check in the settings module to prevent settings values that will exceed this limitation. The maximum
381374
// step rate is strictly limited by the CPU speed and will change if something other than an AVR running

Grbl_Esp32/src/I2SOut.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ static portMUX_TYPE i2s_out_spinlock = portMUX_INITIALIZER_UNLOCKED;
116116
static int i2s_out_initialized = 0;
117117

118118
# ifdef USE_I2S_OUT_STREAM_IMPL
119-
static volatile uint64_t i2s_out_pulse_period;
120-
static uint64_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
119+
static volatile uint32_t i2s_out_pulse_period;
120+
static uint32_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
121121
static volatile i2s_out_pulse_func_t i2s_out_pulse_func;
122122
# endif
123123

@@ -657,10 +657,9 @@ int IRAM_ATTR i2s_out_set_stepping() {
657657
return 0;
658658
}
659659

660-
int IRAM_ATTR i2s_out_set_pulse_period(uint64_t period) {
660+
int IRAM_ATTR i2s_out_set_pulse_period(uint32_t period) {
661661
# ifdef USE_I2S_OUT_STREAM_IMPL
662-
// Use 64-bit values to avoid overflowing during the calculation.
663-
i2s_out_pulse_period = period * 1000000 / F_STEPPER_TIMER;
662+
i2s_out_pulse_period = period;
664663
# endif
665664
return 0;
666665
}

Grbl_Esp32/src/I2SOut.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,9 @@ int i2s_out_set_stepping();
156156
void i2s_out_delay();
157157

158158
/*
159-
Set the pulse callback period in ISR ticks.
160-
(same value of the timer period for the ISR)
159+
Set the pulse callback period in microseconds
161160
*/
162-
int i2s_out_set_pulse_period(uint64_t period);
161+
int i2s_out_set_pulse_period(uint32_t usec);
163162

164163
/*
165164
Register a callback function to generate pulse data

Grbl_Esp32/src/MachineCommon.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
#endif
1616

1717
// ESP32 CPU Settings
18-
const int32_t F_TIMERS = 80000000; // a reference to the speed of ESP32 timers
19-
#define F_STEPPER_TIMER 20000000 // frequency of step pulse timer
18+
const uint32_t fTimers = 80000000; // a reference to the speed of ESP32 timers
2019

2120
// =============== Don't change or comment these out ======================
2221
// They are for legacy purposes and will not affect your I/O

Grbl_Esp32/src/NutsBolts.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ static inline int toMotor2(int axis) {
6161
// Conversions
6262
const double MM_PER_INCH = (25.40);
6363
const double INCH_PER_MM = (0.0393701);
64-
#define TICKS_PER_MICROSECOND (F_STEPPER_TIMER / 1000000) // Different from AVR version
6564

6665
const int DELAY_MODE_DWELL = 0;
6766
const int DELAY_MODE_SYS_SUSPEND = 1;

Grbl_Esp32/src/Stepper.cpp

Lines changed: 35 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,10 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE - 1];
4545
// the planner, where the remaining planner block steps still can.
4646
typedef struct {
4747
uint16_t n_step; // Number of step events to be executed for this segment
48-
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
48+
uint16_t isrPeriod; // Time to next ISR tick, in units of timer ticks
4949
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
50-
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
51-
uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
52-
#else
53-
uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
54-
#endif
55-
uint16_t spindle_rpm; // TODO get rid of this.
50+
uint8_t amass_level; // AMASS level for the ISR to execute this segment
51+
uint16_t spindle_rpm; // TODO get rid of this.
5652
} segment_t;
5753
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
5854

@@ -70,9 +66,7 @@ typedef struct {
7066
uint8_t step_pulse_time; // Step pulse reset time after step rise
7167
uint8_t step_outbits; // The next stepping-bits to be output
7268
uint8_t dir_outbits;
73-
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
7469
uint32_t steps[MAX_N_AXIS];
75-
#endif
7670

7771
uint16_t step_count; // Steps remaining in line segment motion
7872
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
@@ -94,7 +88,7 @@ static volatile uint8_t busy;
9488
static plan_block_t* pl_block; // Pointer to the planner block being prepped
9589
static st_block_t* st_prep_block; // Pointer to the stepper block data being prepped
9690

97-
// esp32 work around for diable in main loop
91+
// esp32 work around for disable in main loop
9892
uint64_t stepper_idle_counter; // used to count down until time to disable stepper drivers
9993
bool stepper_idle;
10094

@@ -243,7 +237,7 @@ static void stepper_pulse_func() {
243237
// Initialize new step segment and load number of steps to execute
244238
st.exec_segment = &segment_buffer[segment_buffer_tail];
245239
// Initialize step segment timing per step and load number of steps to execute.
246-
Stepper_Timer_WritePeriod(st.exec_segment->cycles_per_tick);
240+
Stepper_Timer_WritePeriod(st.exec_segment->isrPeriod);
247241
st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
248242
// If the new segment starts a new planner block, initialize stepper variables and counters.
249243
// NOTE: When the segment data index changes, this indicates a new planner block.
@@ -258,12 +252,10 @@ static void stepper_pulse_func() {
258252
// TODO ABC
259253
}
260254
st.dir_outbits = st.exec_block->direction_bits;
261-
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
262-
// With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
255+
// Adjust Bresenham axis increment counters according to AMASS level.
263256
for (int axis = 0; axis < n_axis; axis++) {
264257
st.steps[axis] = st.exec_block->steps[axis] >> st.exec_segment->amass_level;
265258
}
266-
#endif
267259
// Set real-time spindle output as segment is loaded, just prior to the first step.
268260
spindle->set_rpm(st.exec_segment->spindle_rpm);
269261
} else {
@@ -374,7 +366,7 @@ void st_wake_up() {
374366
// Step pulse delay handling is not require with ESP32...the RMT function does it.
375367
#else // Normal operation
376368
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
377-
st.step_pulse_time = -(((pulse_microseconds->get() - 2) * TICKS_PER_MICROSECOND) >> 3);
369+
st.step_pulse_time = -(((pulse_microseconds->get() - 2) * ticksPerMicrosecond) >> 3);
378370
#endif
379371
// Enable Stepper Driver Interrupt
380372
Stepper_Timer_Start();
@@ -538,20 +530,15 @@ void st_prep_buffer() {
538530
st_prep_block->direction_bits = pl_block->direction_bits;
539531
uint8_t idx;
540532
auto n_axis = number_axis->get();
541-
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
542-
for (idx = 0; idx < n_axis; idx++) {
543-
st_prep_block->steps[idx] = pl_block->steps[idx];
544-
}
545-
st_prep_block->step_event_count = pl_block->step_event_count;
546-
#else
547-
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
548-
// level, such that we never divide beyond the original data anywhere in the algorithm.
533+
534+
// Bit-shift multiply all Bresenham data by the max AMASS level so that
535+
// we never divide beyond the original data anywhere in the algorithm.
549536
// If the original data is divided, we can lose a step from integer roundoff.
550537
for (idx = 0; idx < n_axis; idx++) {
551-
st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL;
538+
st_prep_block->steps[idx] = pl_block->steps[idx] << maxAmassLevel;
552539
}
553-
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
554-
#endif
540+
st_prep_block->step_event_count = pl_block->step_event_count << maxAmassLevel;
541+
555542
// Initialize segment buffer data for generating the segments.
556543
prep.steps_remaining = (float)pl_block->step_event_count;
557544
prep.step_per_mm = prep.steps_remaining / pl_block->millimeters;
@@ -839,49 +826,28 @@ void st_prep_buffer() {
839826
// outputs the exact acceleration and velocity profiles as computed by the planner.
840827

841828
dt += prep.dt_remainder; // Apply previous segment partial step execute time
829+
// dt is in minutes so inv_rate is in minutes
842830
float inv_rate = dt / (last_n_steps_remaining - step_dist_remaining); // Compute adjusted step rate inverse
843831

844832
// Compute CPU cycles per step for the prepped segment.
845-
uint32_t cycles = ceil((TICKS_PER_MICROSECOND * 1000000 * 60) * inv_rate); // (cycles/step)
833+
// fStepperTimer is in units of timerTicks/sec, so the dimensional analysis is
834+
// timerTicks/sec * 60 sec/minute * minutes = timerTicks
835+
uint32_t timerTicks = ceil((fStepperTimer * 60) * inv_rate); // (timerTicks/step)
836+
int level;
846837

847-
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
848838
// Compute step timing and multi-axis smoothing level.
849-
// NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
850-
if (cycles < AMASS_LEVEL1) {
851-
prep_segment->amass_level = 0;
852-
} else {
853-
if (cycles < AMASS_LEVEL2) {
854-
prep_segment->amass_level = 1;
855-
} else if (cycles < AMASS_LEVEL3) {
856-
prep_segment->amass_level = 2;
857-
} else {
858-
prep_segment->amass_level = 3;
859-
}
860-
cycles >>= prep_segment->amass_level;
861-
prep_segment->n_step <<= prep_segment->amass_level;
862-
}
863-
if (cycles < (1UL << 16)) {
864-
prep_segment->cycles_per_tick = cycles; // < 65536 (4.1ms @ 16MHz)
865-
} else {
866-
prep_segment->cycles_per_tick = 0xffff; // Just set the slowest speed possible.
867-
}
868-
#else
869-
// Compute step timing and timer prescalar for normal step generation.
870-
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
871-
prep_segment->prescaler = 1; // prescaler: 0
872-
prep_segment->cycles_per_tick = cycles;
873-
} else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
874-
prep_segment->prescaler = 2; // prescaler: 8
875-
prep_segment->cycles_per_tick = cycles >> 3;
876-
} else {
877-
prep_segment->prescaler = 3; // prescaler: 64
878-
if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
879-
prep_segment->cycles_per_tick = cycles >> 6;
880-
} else { // Just set the slowest speed possible. (Around 4 step/sec.)
881-
prep_segment->cycles_per_tick = 0xffff;
839+
for (level = 0; level < maxAmassLevel; level++) {
840+
if (timerTicks < amassThreshold) {
841+
break;
882842
}
843+
timerTicks >>= 1;
883844
}
884-
#endif
845+
prep_segment->amass_level = level;
846+
prep_segment->n_step <<= level;
847+
// isrPeriod is stored as 16 bits, so limit timerTicks to the
848+
// largest value that will fit in a uint16_t.
849+
prep_segment->isrPeriod = timerTicks > 0xffff ? 0xffff : timerTicks;
850+
885851
// Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it.
886852
segment_buffer_head = segment_next_head;
887853
if (++segment_next_head == SEGMENT_BUFFER_SIZE) {
@@ -935,21 +901,23 @@ float st_get_realtime_rate() {
935901
}
936902
}
937903

938-
void IRAM_ATTR Stepper_Timer_WritePeriod(uint64_t alarm_val) {
904+
// The argument is in units of ticks of the timer that generates ISRs
905+
void IRAM_ATTR Stepper_Timer_WritePeriod(uint16_t timerTicks) {
939906
if (current_stepper == ST_I2S_STREAM) {
940907
#ifdef USE_I2S_STEPS
941-
// 1 tick = F_TIMERS / F_STEPPER_TIMER
908+
// 1 tick = fTimers / fStepperTimer
942909
// Pulse ISR is called for each tick of alarm_val.
943-
i2s_out_set_pulse_period(alarm_val);
910+
// The argument to i2s_out_set_pulse_period is in units of microseconds
911+
i2s_out_set_pulse_period(((uint32_t)timerTicks) / ticksPerMicrosecond);
944912
#endif
945913
} else {
946-
timer_set_alarm_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, alarm_val);
914+
timer_set_alarm_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, (uint64_t)timerTicks);
947915
}
948916
}
949917

950918
void IRAM_ATTR Stepper_Timer_Init() {
951919
timer_config_t config;
952-
config.divider = F_TIMERS / F_STEPPER_TIMER;
920+
config.divider = fTimers / fStepperTimer;
953921
config.counter_dir = TIMER_COUNT_UP;
954922
config.counter_en = TIMER_PAUSE;
955923
config.alarm_en = TIMER_ALARM_EN;

Grbl_Esp32/src/Stepper.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,23 @@ struct PrepFlag {
4646
uint8_t decelOverride : 1;
4747
};
4848

49+
// fStepperTimer should be an integer divisor of the bus speed, i.e. of fTimers
50+
const uint32_t fStepperTimer = 20000000; // frequency of step pulse timer
51+
const int ticksPerMicrosecond = fStepperTimer / 1000000;
52+
4953
// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level
5054
// frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin
5155
// starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
5256
// be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit
5357
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
5458
// Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
59+
// For efficient computation, each cutoff frequency is twice the previous one.
5560
// NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency.
5661
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
5762
// and timer accuracy. Do not alter these settings unless you know what you are doing.
58-
///#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
59-
#define MAX_AMASS_LEVEL 3
60-
// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
61-
// Note ESP32 use F_STEPPER_TIMER rather than the AVR F_CPU
62-
const int AMASS_LEVEL1 = (F_STEPPER_TIMER / 8000); // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
63-
const int AMASS_LEVEL2 = (F_STEPPER_TIMER / 4000); // Over-drives ISR (x4)
64-
const int AMASS_LEVEL3 = (F_STEPPER_TIMER / 2000); // Over-drives ISR (x8)
6563

66-
static_assert(MAX_AMASS_LEVEL >= 1, "AMASS must have 1 or more levels to operate correctly.");
67-
//#endif
64+
const uint32_t amassThreshold = fStepperTimer / 8000;
65+
const int maxAmassLevel = 3; // Each level increase doubles the threshold
6866

6967
const timer_group_t STEP_TIMER_GROUP = TIMER_GROUP_0;
7068
const timer_idx_t STEP_TIMER_INDEX = TIMER_0;
@@ -132,7 +130,7 @@ bool get_stepper_disable(); // returns the state of the pin
132130
void set_stepper_pins_on(uint8_t onMask);
133131
void set_direction_pins_on(uint8_t onMask);
134132

135-
void Stepper_Timer_WritePeriod(uint64_t alarm_val);
133+
void Stepper_Timer_WritePeriod(uint16_t timerTicks);
136134
void Stepper_Timer_Init();
137135
void Stepper_Timer_Start();
138136
void Stepper_Timer_Stop();

0 commit comments

Comments
 (0)