Files
PyCNC/cnc/hal_raspberry/hal.py
T

269 lines
8.6 KiB
Python

import time
from cnc.hal_raspberry import rpgpio
from cnc.pulses import *
from cnc.config import *
from cnc.sensors import thermistor
US_IN_SECONDS = 1000000
gpio = rpgpio.GPIO()
dma = rpgpio.DMAGPIO()
pwm = rpgpio.DMAPWM()
STEP_PIN_MASK_X = 1 << STEPPER_STEP_PIN_X
STEP_PIN_MASK_Y = 1 << STEPPER_STEP_PIN_Y
STEP_PIN_MASK_Z = 1 << STEPPER_STEP_PIN_Z
STEP_PIN_MASK_E = 1 << STEPPER_STEP_PIN_E
def init():
""" Initialize GPIO pins and machine itself.
"""
gpio.init(STEPPER_STEP_PIN_X, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_STEP_PIN_Y, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_STEP_PIN_Z, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_STEP_PIN_E, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_DIR_PIN_X, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_DIR_PIN_Y, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_DIR_PIN_Z, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(STEPPER_DIR_PIN_E, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(ENDSTOP_PIN_X, rpgpio.GPIO.MODE_INPUT_PULLUP)
gpio.init(ENDSTOP_PIN_Y, rpgpio.GPIO.MODE_INPUT_PULLUP)
gpio.init(ENDSTOP_PIN_Z, rpgpio.GPIO.MODE_INPUT_PULLUP)
gpio.init(SPINDLE_PWM_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(FAN_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(EXTRUDER_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.init(BED_HEATER_PIN, rpgpio.GPIO.MODE_OUTPUT)
gpio.clear(SPINDLE_PWM_PIN)
gpio.clear(FAN_PIN)
gpio.clear(EXTRUDER_HEATER_PIN)
gpio.clear(BED_HEATER_PIN)
def spindle_control(percent):
""" Spindle control implementation.
:param percent: spindle speed in percent 0..100. If 0, stop the spindle.
"""
logging.info("spindle control: {}%".format(percent))
if percent > 0:
pwm.add_pin(SPINDLE_PWM_PIN, percent)
else:
pwm.remove_pin(SPINDLE_PWM_PIN)
def fan_control(on_off):
"""
Cooling fan control.
:param on_off: boolean value if fan is enabled.
"""
if on_off:
logging.info("Fan is on")
gpio.set(FAN_PIN)
else:
logging.info("Fan is off")
gpio.clear(FAN_PIN)
def extruder_heater_control(percent):
""" Extruder heater control.
:param percent: heater power in percent 0..100. 0 turns heater off.
"""
if percent > 0:
pwm.add_pin(EXTRUDER_HEATER_PIN, percent)
else:
pwm.remove_pin(EXTRUDER_HEATER_PIN)
def bed_heater_control(percent):
""" Hot bed heater control.
:param percent: heater power in percent 0..100. 0 turns heater off.
"""
if percent > 0:
pwm.add_pin(BED_HEATER_PIN, percent)
else:
pwm.remove_pin(BED_HEATER_PIN)
def get_extruder_temperature():
""" Measure extruder temperature.
:return: temperature in Celsius.
"""
return thermistor.get_temperature(EXTRUDER_TEMPERATURE_SENSOR_CHANNEL)
def get_bed_temperature():
""" Measure bed temperature.
:return: temperature in Celsius.
"""
return thermistor.get_temperature(BED_TEMPERATURE_SENSOR_CHANNEL)
def calibrate(x, y, z):
""" Move head to home position till end stop switch will be triggered.
Do not return till all procedures are completed.
:param x: boolean, True to calibrate X axis.
:param y: boolean, True to calibrate Y axis.
:param z: boolean, True to calibrate Z axis.
:return: boolean, True if all specified end stops were triggered.
"""
logging.info("hal calibrate, x={}, y={}, z={}".format(x, y, z))
if STEPPER_INVERTED_X:
gpio.clear(STEPPER_DIR_PIN_X)
else:
gpio.set(STEPPER_DIR_PIN_X)
if STEPPER_INVERTED_Y:
gpio.clear(STEPPER_DIR_PIN_Y)
else:
gpio.set(STEPPER_DIR_PIN_Y)
if STEPPER_INVERTED_Z:
gpio.clear(STEPPER_DIR_PIN_Z)
else:
gpio.set(STEPPER_DIR_PIN_Z)
pins = 0
max_size = 0
if x:
pins |= STEP_PIN_MASK_X
max_size = max(max_size, TABLE_SIZE_X_MM * STEPPER_PULSES_PER_MM_X)
if y:
pins |= STEP_PIN_MASK_Y
max_size = max(max_size, TABLE_SIZE_Y_MM * STEPPER_PULSES_PER_MM_Y)
if z:
pins |= STEP_PIN_MASK_Z
max_size = max(max_size, TABLE_SIZE_Z_MM * TABLE_SIZE_Z_MM)
pulses_per_mm_avg = (STEPPER_PULSES_PER_MM_X + STEPPER_PULSES_PER_MM_Y
+ STEPPER_PULSES_PER_MM_Z) / 3.0
pulses_per_sec = CALIBRATION_VELOCITY_MM_PER_MIN / 60.0 * pulses_per_mm_avg
end_time = time.time() + 1.2 * max_size / pulses_per_sec
last_pins = ~pins
try:
while time.time() < end_time:
# check each axis end stop twice
x_endstop = (STEP_PIN_MASK_X & pins) != 0
y_endstop = (STEP_PIN_MASK_Y & pins) != 0
z_endstop = (STEP_PIN_MASK_Z & pins) != 0
# read each sensor three time
for _ in range(0, 3):
x_endstop = x_endstop and ((gpio.read(ENDSTOP_PIN_X) == 1)
== ENDSTOP_INVERTED_X)
y_endstop = y_endstop and ((gpio.read(ENDSTOP_PIN_Y) == 1)
== ENDSTOP_INVERTED_Y)
z_endstop = z_endstop and ((gpio.read(ENDSTOP_PIN_Z) == 1)
== ENDSTOP_INVERTED_Z)
if x_endstop:
pins &= ~STEP_PIN_MASK_X
if y_endstop:
pins &= ~STEP_PIN_MASK_Y
if z_endstop:
pins &= ~STEP_PIN_MASK_Z
if pins != last_pins:
dma.stop()
dma.clear()
if pins == 0:
return True
dma.add_pulse(pins, STEPPER_PULSE_LENGTH_US)
# limit velocity
dma.add_delay(int(1000000 / pulses_per_sec))
last_pins = pins
dma.run(True)
except KeyboardInterrupt:
dma.stop()
return False
def move(generator):
""" Move head to specified position
:param generator: PulseGenerator object.
"""
# wait if previous command still works
while dma.is_active():
time.sleep(0.001)
# prepare and run dma
dma.clear()
prev = 0
is_ran = False
instant = INSTANT_RUN
st = time.time()
for direction, tx, ty, tz, te in generator:
if direction: # set up directions
pins_to_set = 0
pins_to_clear = 0
if tx > 0:
pins_to_clear |= 1 << STEPPER_DIR_PIN_X
elif tx < 0:
pins_to_set |= 1 << STEPPER_DIR_PIN_X
if ty > 0:
pins_to_clear |= 1 << STEPPER_DIR_PIN_Y
elif ty < 0:
pins_to_set |= 1 << STEPPER_DIR_PIN_Y
if tz > 0:
pins_to_clear |= 1 << STEPPER_DIR_PIN_Z
elif tz < 0:
pins_to_set |= 1 << STEPPER_DIR_PIN_Z
if te > 0:
pins_to_clear |= 1 << STEPPER_DIR_PIN_E
elif te < 0:
pins_to_set |= 1 << STEPPER_DIR_PIN_E
dma.add_set_clear(pins_to_set, pins_to_clear)
continue
pins = 0
m = None
for i in (tx, ty, tz, te):
if i is not None and (m is None or i < m):
m = i
k = int(round(m * US_IN_SECONDS))
if tx is not None:
pins |= STEP_PIN_MASK_X
if ty is not None:
pins |= STEP_PIN_MASK_Y
if tz is not None:
pins |= STEP_PIN_MASK_Z
if te is not None:
pins |= STEP_PIN_MASK_E
if k - prev > 0:
dma.add_delay(k - prev)
dma.add_pulse(pins, STEPPER_PULSE_LENGTH_US)
# TODO not a precise way! pulses will set in queue, instead of crossing
# if next pulse start during pulse length. Though it almost doesn't
# matter for pulses with 1-2us length.
prev = k + STEPPER_PULSE_LENGTH_US
# instant run handling
if not is_ran and instant:
if k > 500000: # wait at least 500 ms is uploaded
if time.time() - st > 0.5:
logging.warn("Buffer preparing for instant run took more "
"time then buffer time")
instant = False
else:
dma.run_stream()
is_ran = True
pt = time.time()
if not is_ran:
dma.run(False)
else:
dma.finalize_stream()
logging.info("prepared in " + str(round(pt - st, 2)) + "s, estimated in "
+ str(round(generator.total_time_s(), 2)) + "s")
def join():
""" Wait till motors work.
"""
logging.info("hal join()")
# wait till dma works
while dma.is_active():
time.sleep(0.01)
def deinit():
""" De-initialize hardware.
"""
join()
pwm.remove_all()
gpio.clear(SPINDLE_PWM_PIN)
gpio.clear(FAN_PIN)
gpio.clear(EXTRUDER_HEATER_PIN)
gpio.clear(BED_HEATER_PIN)