m84 real implementation, refactoring: mostly line width

This commit is contained in:
Nikolay Khabarov
2017-07-09 04:12:31 +03:00
parent 129d26ed5e
commit f549801bdd
12 changed files with 72 additions and 34 deletions

View File

@@ -32,8 +32,9 @@ Video demo - [YouTube video](https://youtu.be/vcedo59raS4)
# Current gcode support # Current gcode support
Commands G0, G1, G2, G3, G4, G17, G18, G19, G20, G21, G28, G53, G90, G91, G92, Commands G0, G1, G2, G3, G4, G17, G18, G19, G20, G21, G28, G53, G90, G91, G92,
M2, M3, M5, M30, M104, M105, M106, M107, M109, M114, M140, M190 are supported. M2, M3, M5, M30, M84, M104, M105, M106, M107, M109, M114, M140, M190 are
Commands can be easily added, see [gmachine.py](./cnc/gmachine.py) file. supported. Commands can be easily added, see [gmachine.py](./cnc/gmachine.py)
file.
Four axis are supported - X, Y, Z, E. Four axis are supported - X, Y, Z, E.
Circular interpolation for XY, ZX, YZ planes is supported. Circular interpolation for XY, ZX, YZ planes is supported.
Spindle with rpm control is supported. Spindle with rpm control is supported.

View File

@@ -52,6 +52,8 @@ BED_PID = {"P": 5.06820175723,
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Pins configuration. # Pins configuration.
# Enable pin for all steppers, low level is enabled.
STEPPERS_ENABLE_PIN = 26
STEPPER_STEP_PIN_X = 16 STEPPER_STEP_PIN_X = 16
STEPPER_STEP_PIN_Y = 20 STEPPER_STEP_PIN_Y = 20
STEPPER_STEP_PIN_Z = 21 STEPPER_STEP_PIN_Z = 21

View File

@@ -220,8 +220,8 @@ class GMachine(object):
logging.info("Moving circularly {} {} {} with radius {}" logging.info("Moving circularly {} {} {} with radius {}"
" and velocity {}".format(self._plane, circle_end, " and velocity {}".format(self._plane, circle_end,
direction, radius, velocity)) direction, radius, velocity))
gen = PulseGeneratorCircular(circle_end, radius, self._plane, direction, gen = PulseGeneratorCircular(circle_end, radius, self._plane,
velocity) direction, velocity)
self.__check_velocity(gen.max_velocity()) self.__check_velocity(gen.max_velocity())
# if finish coords is not on circle, move some distance linearly # if finish coords is not on circle, move some distance linearly
linear_delta = delta - circle_end linear_delta = delta - circle_end
@@ -411,6 +411,8 @@ class GMachine(object):
self._spindle(0) self._spindle(0)
elif c == 'M2' or c == 'M30': # program finish, reset everything. elif c == 'M2' or c == 'M30': # program finish, reset everything.
self.reset() self.reset()
elif c == 'M84': # disable motors
hal.disable_steppers()
# extruder and bed heaters control # extruder and bed heaters control
elif c == 'M104' or c == 'M109' or c == 'M140' or c == 'M190': elif c == 'M104' or c == 'M109' or c == 'M140' or c == 'M190':
if c == 'M104' or c == 'M109': if c == 'M104' or c == 'M109':
@@ -453,7 +455,7 @@ class GMachine(object):
hal.join() hal.join()
p = self.position() p = self.position()
answer = "X:{} Y:{} Z:{} E:{}".format(p.x, p.y, p.z, p.e) answer = "X:{} Y:{} Z:{} E:{}".format(p.x, p.y, p.z, p.e)
elif c is None: # command not specified(for example, just F was passed) elif c is None: # command not specified(ie just F was passed)
pass pass
# commands below are added just for compatibility # commands below are added just for compatibility
elif c == 'M82': # absolute mode for extruder elif c == 'M82': # absolute mode for extruder
@@ -462,8 +464,6 @@ class GMachine(object):
elif c == 'M83': # relative mode for extruder elif c == 'M83': # relative mode for extruder
if self._absoluteCoordinates: if self._absoluteCoordinates:
raise GMachineException("Not supported, use G90/G91") raise GMachineException("Not supported, use G90/G91")
elif c == 'M84': # disable motors
pass # do not do anything
else: else:
raise GMachineException("unknown command") raise GMachineException("unknown command")
# save parameters on success # save parameters on success

View File

@@ -51,6 +51,13 @@
# """ # """
# return measure() # return measure()
# #
#
# def disable_steppers():
# """ Disable all steppers until any movement occurs.
# """
# do_something()
#
#
# def calibrate(x, y, z): # def calibrate(x, y, z):
# """ Move head to home position till end stop switch will be triggered. # """ Move head to home position till end stop switch will be triggered.
# Do not return till all procedures are completed. # Do not return till all procedures are completed.
@@ -61,6 +68,7 @@
# """ # """
# return do_something() # return do_something()
# #
#
# def move(generator): # def move(generator):
# """ Move head to according pulses in PulseGenerator. # """ Move head to according pulses in PulseGenerator.
# :param generator: PulseGenerator object # :param generator: PulseGenerator object
@@ -103,6 +111,8 @@ if 'get_extruder_temperature' not in locals():
raise NotImplementedError("hal.get_extruder_temperature() not implemented") raise NotImplementedError("hal.get_extruder_temperature() not implemented")
if 'get_bed_temperature' not in locals(): if 'get_bed_temperature' not in locals():
raise NotImplementedError("hal.get_bed_temperature() not implemented") raise NotImplementedError("hal.get_bed_temperature() not implemented")
if 'disable_steppers' not in locals():
raise NotImplementedError("hal.disable_steppers() not implemented")
if 'calibrate' not in locals(): if 'calibrate' not in locals():
raise NotImplementedError("hal.calibrate() not implemented") raise NotImplementedError("hal.calibrate() not implemented")
if 'move' not in locals(): if 'move' not in locals():

View File

@@ -35,10 +35,12 @@ def init():
gpio.init(FAN_PIN, rpgpio.GPIO.MODE_OUTPUT) gpio.init(FAN_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(EXTRUDER_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT) gpio.init(EXTRUDER_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(BED_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT) gpio.init(BED_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPERS_ENABLE_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.clear(SPINDLE_PWM_PIN) gpio.clear(SPINDLE_PWM_PIN)
gpio.clear(FAN_PIN) gpio.clear(FAN_PIN)
gpio.clear(EXTRUDER_HEATER_PIN) gpio.clear(EXTRUDER_HEATER_PIN)
gpio.clear(BED_HEATER_PIN) gpio.clear(BED_HEATER_PIN)
gpio.clear(STEPPERS_ENABLE_PIN)
def spindle_control(percent): def spindle_control(percent):
@@ -99,6 +101,13 @@ def get_bed_temperature():
return thermistor.get_temperature(BED_TEMPERATURE_SENSOR_CHANNEL) return thermistor.get_temperature(BED_TEMPERATURE_SENSOR_CHANNEL)
def disable_steppers():
""" Disable all steppers until any movement occurs.
"""
logging.info("disable steppers")
gpio.set(STEPPERS_ENABLE_PIN)
def __calibrate_private(x, y, z, invert): def __calibrate_private(x, y, z, invert):
if invert: if invert:
stepper_inverted_x = not STEPPER_INVERTED_X stepper_inverted_x = not STEPPER_INVERTED_X
@@ -190,6 +199,8 @@ def calibrate(x, y, z):
:param z: boolean, True to calibrate Z axis. :param z: boolean, True to calibrate Z axis.
:return: boolean, True if all specified end stops were triggered. :return: boolean, True if all specified end stops were triggered.
""" """
# enable steppers
gpio.clear(STEPPERS_ENABLE_PIN)
logging.info("hal calibrate, x={}, y={}, z={}".format(x, y, z)) logging.info("hal calibrate, x={}, y={}, z={}".format(x, y, z))
if not __calibrate_private(x, y, z, True): # move from endstop switch if not __calibrate_private(x, y, z, True): # move from endstop switch
return False return False
@@ -206,6 +217,8 @@ def move(generator):
# moving. In this case machine would safely paused between commands until # moving. In this case machine would safely paused between commands until
# calculation is done. # calculation is done.
# enable steppers
gpio.clear(STEPPERS_ENABLE_PIN)
# 4 control blocks per 32 bytes # 4 control blocks per 32 bytes
bytes_per_iter = 4 * dma.control_block_size() bytes_per_iter = 4 * dma.control_block_size()
# prepare and run dma # prepare and run dma
@@ -309,6 +322,7 @@ def deinit():
""" De-initialize hardware. """ De-initialize hardware.
""" """
join() join()
disable_steppers()
pwm.remove_all() pwm.remove_all()
gpio.clear(SPINDLE_PWM_PIN) gpio.clear(SPINDLE_PWM_PIN)
gpio.clear(FAN_PIN) gpio.clear(FAN_PIN)

View File

@@ -15,8 +15,8 @@ class GPIO(object):
def __init__(self): def __init__(self):
""" Create object which can control GPIO. """ Create object which can control GPIO.
This class writes directly to CPU registers and doesn't use any libs This class writes directly to CPU registers and doesn't use any
or kernel modules. libs or kernel modules.
""" """
self._mem = PhysicalMemory(PERI_BASE + GPIO_REGISTER_BASE) self._mem = PhysicalMemory(PERI_BASE + GPIO_REGISTER_BASE)
@@ -191,7 +191,7 @@ class DMAGPIO(DMAProto):
self._phys_memory.write_int(self.__current_address + 20 self._phys_memory.write_int(self.__current_address + 20
- self._DMA_CONTROL_BLOCK_SIZE, 0) - self._DMA_CONTROL_BLOCK_SIZE, 0)
logging.info("DMA took {}MB of memory". logging.info("DMA took {}MB of memory".
format(round(self.__current_address / 1024.0 / 1024.0, 2))) format(round(self.__current_address / 1048576.0, 2)))
def run_stream(self): def run_stream(self):
""" Run DMA module in stream mode, i.e. does'n finalize last block """ Run DMA module in stream mode, i.e. does'n finalize last block
@@ -199,18 +199,19 @@ class DMAGPIO(DMAProto):
""" """
# configure PWM hardware module which will clocks DMA # configure PWM hardware module which will clocks DMA
self._pwm.write_int(PWM_CTL, 0) self._pwm.write_int(PWM_CTL, 0)
self._clock.write_int(CM_PWM_CNTL, CM_PASSWORD | CM_SRC_PLLD) # disable # disable
self._clock.write_int(CM_PWM_CNTL, CM_PASSWORD | CM_SRC_PLLD)
while (self._clock.read_int(CM_PWM_CNTL) & CM_CNTL_BUSY) != 0: while (self._clock.read_int(CM_PWM_CNTL) & CM_CNTL_BUSY) != 0:
time.sleep(0.00001) # 10 us, wait until BUSY bit is clear time.sleep(0.00001) # 10 us, wait until BUSY bit is clear
self._clock.write_int(CM_PWM_DIV, # configure, 100 MHz
CM_PASSWORD | CM_DIV_VALUE(5)) # 100MHz self._clock.write_int(CM_PWM_DIV, CM_PASSWORD | CM_DIV_VALUE(5))
self._clock.write_int(CM_PWM_CNTL, self._clock.write_int(CM_PWM_CNTL,
CM_PASSWORD | CM_SRC_PLLD | CM_CNTL_ENABLE) CM_PASSWORD | CM_SRC_PLLD | CM_CNTL_ENABLE)
self._pwm.write_int(PWM_RNG1, 100) self._pwm.write_int(PWM_RNG1, 100)
self._pwm.write_int(PWM_DMAC, PWM_DMAC_ENAB self._pwm.write_int(PWM_DMAC, PWM_DMAC_ENAB | PWM_DMAC_PANIC(15)
| PWM_DMAC_PANIC(15) | PWM_DMAC_DREQ(15)) | PWM_DMAC_DREQ(15))
self._pwm.write_int(PWM_CTL, PWM_CTL_CLRF) self._pwm.write_int(PWM_CTL, PWM_CTL_CLRF)
# enable
self._pwm.write_int(PWM_CTL, PWM_CTL_USEF1 | PWM_CTL_PWEN1) self._pwm.write_int(PWM_CTL, PWM_CTL_USEF1 | PWM_CTL_PWEN1)
super(DMAGPIO, self)._run_dma() super(DMAGPIO, self)._run_dma()

View File

@@ -62,6 +62,12 @@ def get_bed_temperature():
return BED_MAX_TEMPERATURE * 0.999 return BED_MAX_TEMPERATURE * 0.999
def disable_steppers():
""" Disable all steppers until any movement occurs.
"""
logging.info("hal disable steppers")
def calibrate(x, y, z): def calibrate(x, y, z):
""" Move head to home position till end stop switch will be triggered. """ Move head to home position till end stop switch will be triggered.
Do not return till all procedures are completed. Do not return till all procedures are completed.

View File

@@ -61,8 +61,8 @@ class Heater(threading.Thread):
last_error = time.time() last_error = time.time()
else: else:
if time.time() - last_error > self.SENSOR_TIMEOUT_S: if time.time() - last_error > self.SENSOR_TIMEOUT_S:
logging.critical("No data from temperature sensor. Stop" logging.critical("No data from temperature sensor."
" heating.") " Stop heating.")
break break
continue continue
last_error = None last_error = None
@@ -87,8 +87,8 @@ class Heater(threading.Thread):
i = 0 i = 0
while not self._pid.is_fixed(): while not self._pid.is_fixed():
if i % 8 == 0: if i % 8 == 0:
logging.info("Heating... current temperature {} C, power {}%" logging.info("Heating... current temperature {} C, power {}%".
.format(self._measure(), int(self._current_power))) format(self._measure(), int(self._current_power)))
i = 0 i = 0
i += 1 i += 1
time.sleep(0.25) time.sleep(0.25)

View File

@@ -609,9 +609,10 @@ class PulseGeneratorCircular(PulseGenerator):
def __circular_a(self, i, pulses_per_mm): def __circular_a(self, i, pulses_per_mm):
if i >= self._iterations_a: if i >= self._iterations_a:
return self._dir_a, None return self._dir_a, None
a, direction, side = self.__circular_helper(self._start_a_pulses, i + 1, a, direction, side = \
self._radius_a_pulses, self.__circular_helper(self._start_a_pulses, i + 1,
self._side_a, self._dir_a) self._radius_a_pulses,
self._side_a, self._dir_a)
a /= pulses_per_mm a /= pulses_per_mm
# first and last item can be slightly out of bound due float precision # first and last item can be slightly out of bound due float precision
if i + 1 == self._iterations_a: if i + 1 == self._iterations_a:
@@ -624,9 +625,10 @@ class PulseGeneratorCircular(PulseGenerator):
def __circular_b(self, i, pulses_per_mm): def __circular_b(self, i, pulses_per_mm):
if i >= self._iterations_b: if i >= self._iterations_b:
return self._dir_b, None return self._dir_b, None
b, direction, side = self.__circular_helper(self._start_b_pulses, i + 1, b, direction, side = \
self._radius_b_pulses, self.__circular_helper(self._start_b_pulses, i + 1,
self._side_b, self._dir_b) self._radius_b_pulses,
self._side_b, self._dir_b)
b /= pulses_per_mm b /= pulses_per_mm
# first and last item can be slightly out of bound due float precision # first and last item can be slightly out of bound due float precision
if i + 1 == self._iterations_b: if i + 1 == self._iterations_b:

View File

@@ -78,7 +78,8 @@ class TestGMachine(unittest.TestCase):
self.assertRaises(GMachineException, self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G1X1F-1")) m.do_command, GCode.parse_line("G1X1F-1"))
cl = "G1X1F" + str(MIN_VELOCITY_MM_PER_MIN - 0.0000001) cl = "G1X1F" + str(MIN_VELOCITY_MM_PER_MIN - 0.0000001)
self.assertRaises(GMachineException, m.do_command, GCode.parse_line(cl)) self.assertRaises(GMachineException, m.do_command,
GCode.parse_line(cl))
m.do_command(GCode.parse_line("G1X100F" m.do_command(GCode.parse_line("G1X100F"
+ str(MAX_VELOCITY_MM_PER_MIN_X))) + str(MAX_VELOCITY_MM_PER_MIN_X)))
m.do_command(GCode.parse_line("G1Y100F" m.do_command(GCode.parse_line("G1Y100F"
@@ -120,7 +121,8 @@ class TestGMachine(unittest.TestCase):
m.do_command, GCode.parse_line("G2X99999999Y99999999" m.do_command, GCode.parse_line("G2X99999999Y99999999"
"I1J1")) "I1J1"))
self.assertRaises(GMachineException, self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G2X2Y2Z99999999I1J1")) m.do_command,
GCode.parse_line("G2X2Y2Z99999999I1J1"))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 0)) self.assertEqual(m.position(), Coordinates(0, 0, 0, 0))
self.assertRaises(GMachineException, self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G2X4Y4I2J2")) m.do_command, GCode.parse_line("G2X4Y4I2J2"))
@@ -250,9 +252,9 @@ class TestGMachine(unittest.TestCase):
self.assertEqual(m.extruder_target_temperature(), 0) self.assertEqual(m.extruder_target_temperature(), 0)
self.assertRaises(GMachineException, m.do_command, self.assertRaises(GMachineException, m.do_command,
GCode.parse_line("M104S"+str(MIN_TEMPERATURE - 1))) GCode.parse_line("M104S"+str(MIN_TEMPERATURE - 1)))
et = EXTRUDER_MAX_TEMPERATURE + 1
self.assertRaises(GMachineException, m.do_command, self.assertRaises(GMachineException, m.do_command,
GCode.parse_line("M109S" GCode.parse_line("M109S" + str(et)))
+ str(EXTRUDER_MAX_TEMPERATURE + 1)))
self.assertRaises(GMachineException, m.do_command, self.assertRaises(GMachineException, m.do_command,
GCode.parse_line("M109")) GCode.parse_line("M109"))

View File

@@ -64,8 +64,8 @@ class TestHeater(unittest.TestCase):
def test_fail(self): def test_fail(self):
# check if heater will not fix with incorrect temperature # check if heater will not fix with incorrect temperature
self._control_counter = 0 self._control_counter = 0
he = Heater(self._target_temp, EXTRUDER_PID, self.__get_bad_temperature, he = Heater(self._target_temp, EXTRUDER_PID,
self.__control) self.__get_bad_temperature, self.__control)
j = 0 j = 0
while self._control_counter < 10: while self._control_counter < 10:
time.sleep(0.01) time.sleep(0.01)

View File

@@ -10,8 +10,8 @@ from cnc.hal_raspberry import hal
""" """
This executable module is looking for heating and cooling transfer coefficients. This executable module is looking for heating and cooling transfer
Can be ran only on real hardware. coefficients. Can be ran only on real hardware.
""" """
# change settings below for your hardware/environment # change settings below for your hardware/environment