Skip to content

Threading Code explanation

Huub Buis edited this page Oct 14, 2021 · 19 revisions

Index pulse detecting:

Index pulses are connected to the Y-limit pin and processed by PCINT0. On the receive of an index pulse, a flag is set for protocol.protocol_exec_rt_system() to process the index pulse.

Synchronization pulse detecting:

Synchronization pulses are connected to the probe pin and processed by PCINT2. On the receive of a synchronization pulse, if (settings.sync_pulses_per_revolution>1), a flag is set for protocol.protocol_exec_rt_system() to process the synchronization pulse.

G33 Gcode line processing:

When a G33 Gcode line is processed by the Gcode parser:

  • Waits until the planner buffer is empty. All movements will stop.
  • Resets the index pulse counter, synchronization pulse counter and timer.
  • Waits until 3 index pulses are received. If the time between any of the 3 index pulses exceeds 6 seconds, error 18 is reported and the Gcode line is ignored. During this wait, protocol.protocol_exec_rt_system() is called so GRBL keeps responding.
  • Calculates the initial feed rate based on the time passed between the last 2 index pulses.
  • Waits for a maximum of 6 seconds for the next synchronization pulse. On a timeout, error 19 is reported. During this wait, protocol.protocol_exec_rt_system() is called so GRBL keeps responding.
  • Adds the G33 line to the planner buffer. Because the planner buffer is empty, this G33 line will be executed immediately.
    When a G33 planner block is active, the grbl serial buffer will not be processed except for real time commands like Abort and real time status requests. This reduces the time to recalculate the feed rates during the receive of spindle synchronization pulses.

Keeping synchronized

For every synchronization pulse the tool has to move the distance as specified in the G33 K parameter divided by the number of synchronization pulses per rotation (threading_mm_per_synchronization_pulse).
At every synchronization pulse, the time it happened is recorded and the pulses are. The stepper interrupt counts the Z-steps so that the planner can compare the actual spindle position and the Z-axis position. The difference is the synchronization error (synchronization_millimeters_error).
At every synchronization pulse, the time passed since the last synchronization pulse is calculated (threading_sync_timer_tics_passed). This value represents the actual spindle speed and assumably, the next synchronization pulse will take the "same" amount of time.
To eliminate the synchronization error, the feed rate has to be set (b_lock->programmed_rate_) so that at the next synchronization pulse, the distance traveled equals threading_mm_per_synchronization_pulse + synchronization_millimeters_error. The next synchronization pulse is expected in assumably threading_sync_timer_tics_passed time. Based on this, the feed rate is calculated and set to block->programmed_rate.
Now the planner block is updated, the actual feed rate is set by calling planner.plan_update_velocity_profile_parameters() followed by planner.plan_cycle_reinitialize(). By calculating the synchronization error at every synchronization pulse and compensate for it, errors are not accumulated and even long threads will be accurate!

Program flow

protocol.protocol_exec_rt_system() is the grbl real time processing engine. It takes care of handling tasks signaled by flags that are set as response to received interrupts and serial data.
When a process index pulse flag is set and (settings.sync_pulses_per_revolution>0), threading_index_pulse_count is increased, the time passed since the previous index pulse is calculated and the actual spindle speed is calculated. If a G33 planner block is active, a flag is set for report.report() to report the spindle speed and synchronization error. If (settings.sync_pulses_per_revolution==1) the receive of a synchronization pulse is simulated by setting process synchronization pulse flag.
When a process synchronization pulse flag is set, threading_synchronization_pulse_count is increased and the time passed since the previous synchronization pulse is calculated. If a G33 planner block is active, a new feed rate is calculated and the planner is updated to set the new feed rate.

memory used

To reduce the extra ram memory as much as possible, variables that logically should be placed in the planner block, are defined globally. The threading code uses about 2400 bytes of extra program memory and 81 bytes of extra ram memory. By reducing the planner block buffer from 36 to 34 blocks, this version uses 29 bytes less ram memory.

Timer

The timer is a 16 bit timer running at 4 u seconds per timer tic. The overflows are counted and together they form a 32 bit 4us timer tic counter.

Code changes

build_option_codes_en_US.csv

error_codes_en_US.csv

setting_codes_en_US.csv

config.h

cpu_map.h

defaults.h

gcode.c

gcode.h

grbl.h

limits.c

limits.h

main.c

motion_control.c

planner.c

planner.h

protocol.c

report.c

report.h

serial.c

serial.h

settings.c

settings.h

system.c

system.h

threading.c

threading.h

time_keeper.c

time_keeper.h

timekeeper.c

threading.c