aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2017-08-25 23:31:58 -0400
committerKevin O'Connor <kevin@koconnor.net>2017-09-20 13:18:07 -0400
commit3b4088c23fc742334f5bd9daf9bbf98e7e89daac (patch)
tree009e520d09981f605ba1fde3b281da3e9252d6bd
parent5a85c1667a80cc2dda9861980c64d99f8da0163e (diff)
downloadkutter-3b4088c23fc742334f5bd9daf9bbf98e7e89daac.tar.gz
kutter-3b4088c23fc742334f5bd9daf9bbf98e7e89daac.tar.xz
kutter-3b4088c23fc742334f5bd9daf9bbf98e7e89daac.zip
chipmisc: Add support for Replicape board
Add support for configuring and controlling the hardware specific to the revision "B3" Replicape board. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--config/example-extras.cfg5
-rw-r--r--config/generic-replicape.cfg114
-rw-r--r--docs/beaglebone.md14
-rw-r--r--klippy/chipmisc.py174
4 files changed, 269 insertions, 38 deletions
diff --git a/config/example-extras.cfg b/config/example-extras.cfg
index 935a3d52..7b943218 100644
--- a/config/example-extras.cfg
+++ b/config/example-extras.cfg
@@ -122,3 +122,8 @@
# its highest resistance, and then the 'channel_x' parameters can be
# specified using the desired amperage value for the stepper. The
# default is to not scale the 'channel_x' parameters.
+
+
+# Replicape support - see the generic-replicape.cfg file for further
+# details.
+#[replicape]
diff --git a/config/generic-replicape.cfg b/config/generic-replicape.cfg
index 4095eace..2677973b 100644
--- a/config/generic-replicape.cfg
+++ b/config/generic-replicape.cfg
@@ -1,14 +1,15 @@
-# This file contains an example configuration for the Beaglebone PRU
-# micro-controller.
-
-# THIS FILE HAS NOT BEEN TESTED - PROCEED WITH CAUTION!
+# This file contains an example configuration for the Replicape rev B3
+# board. To use this config, one must compile and install the
+# micro-controller code for the "Beaglebone PRU", and then compile and
+# install the micro-controller code a second time for a "Linux
+# process".
# See the example.cfg file for a description of available parameters.
[stepper_x]
step_pin: P8_17
dir_pin: P8_26
-enable_pin: !P9_41
+enable_pin: replicape:stepper_x_enable
step_distance: .0125
endstop_pin: ^P9_25
position_endstop: 0
@@ -18,7 +19,7 @@ homing_speed: 50
[stepper_y]
step_pin: P8_12
dir_pin: P8_19
-enable_pin: !P9_41
+enable_pin: replicape:stepper_y_enable
step_distance: .0125
endstop_pin: ^P9_23
position_endstop: 0
@@ -28,42 +29,39 @@ homing_speed: 50
[stepper_z]
step_pin: P8_13
dir_pin: P8_14
-enable_pin: !P9_41
+enable_pin: replicape:stepper_z_enable
step_distance: 0.00025
endstop_pin: ^P9_13
position_endstop: 0
position_max: 200
-# XXX - Extruder heater hooked up to i2c mosfet
-#[extruder]
-#step_pin: P9_12
-#dir_pin: P8_15
-#enable_pin: !P9_41
-#step_distance: .002
-#nozzle_diameter: 0.400
-#filament_diameter: 1.750
-#heater_pin: ?
-#sensor_type: EPCOS 100K B57560G104F
-#sensor_pin: P9_39
-#control: pid
-#pid_Kp: 22.2
-#pid_Ki: 1.08
-#pid_Kd: 114
-#min_temp: 0
-#max_temp: 250
+[extruder]
+step_pin: P9_12
+dir_pin: P8_15
+enable_pin: replicape:stepper_e_enable
+step_distance: .002
+nozzle_diameter: 0.400
+filament_diameter: 1.750
+heater_pin: replicape:power_e
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: host:analog4
+control: pid
+pid_Kp: 22.2
+pid_Ki: 1.08
+pid_Kd: 114
+min_temp: 0
+max_temp: 250
-# XXX - Bed heater hooked up to i2c mosfet
-#[heater_bed]
-#heater_pin: ?
-#sensor_type: EPCOS 100K B57560G104F
-#sensor_pin: P9_40
-#control: watermark
-#min_temp: 0
-#max_temp: 130
+[heater_bed]
+heater_pin: replicape:power_hotbed
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: host:analog6
+control: watermark
+min_temp: 0
+max_temp: 130
-# XXX - Fan power hooked up to i2c mosfet
-#[fan]
-#pin: ?
+[fan]
+pin: replicape:power_fan0
[mcu]
serial: /dev/rpmsg_pru30
@@ -73,5 +71,47 @@ pin_map: beaglebone
kinematics: cartesian
max_velocity: 300
max_accel: 3000
-max_z_velocity: 5
-max_z_accel: 100
+max_z_velocity: 25
+max_z_accel: 30
+
+[mcu host]
+serial: /tmp/klipper_host_mcu
+
+# The "replicape" config section adds "replicape:stepper_x_enable"
+# virtual stepper enable pins (for steppers x, y, z, e, and h) and
+# "replicape:power_x" PWM output pins (for hotbed, e, h, fan0, fan1,
+# fan2, and fan3) that may then be used elsewhere in the config file.
+[replicape]
+revision: B3
+# The replicape hardware revision. Currently only revision "B3" is
+# supported. This parameter must be provided.
+#enable_pin: !P9_41
+# The replicape global enable pin. The default is !P9_41.
+host_mcu: host
+# The name of the mcu config section that communicates with the
+# Klipper "linux process" mcu instance. This parameter must be
+# provided.
+stepper_x_microstep_mode: spread16
+# This parameter controls the CFG1 and CFG2 pins of the given
+# stepper motor driver. Available options are: disable, 1, 2,
+# spread2, 4, 16, spread4, spread16, stealth4, and stealth16. The
+# default is disable.
+stepper_x_current: 0.5
+# The configured maximum current (in Amps) of the stepper motor
+# driver. This parameter must be provided if the stepper is not in a
+# disable mode.
+#stepper_x_chopper_off_time_high: False
+# This parameter controls the CFG0 pin of the stepper motor driver
+# (True sets CFG0 high, False sets it low). The default is False.
+#stepper_x_chopper_hysteresis_high: False
+# This parameter controls the CFG4 pin of the stepper motor driver
+# (True sets CFG4 high, False sets it low). The default is False.
+#stepper_x_chopper_blank_time_high: True
+# This parameter controls the CFG5 pin of the stepper motor driver
+# (True sets CFG5 high, False sets it low). The default is True.
+stepper_y_microstep_mode: spread16
+stepper_y_current: 0.5
+stepper_z_microstep_mode: spread16
+stepper_z_current: 0.5
+stepper_e_microstep_mode: 16
+stepper_e_current: 0.5
diff --git a/docs/beaglebone.md b/docs/beaglebone.md
index 2eea47a8..d3e1b000 100644
--- a/docs/beaglebone.md
+++ b/docs/beaglebone.md
@@ -71,6 +71,20 @@ make flash
sudo service klipper start
```
+For the Replicape, it is also necessary to compile and install the
+micro-controller code for a Linux host process. Run "make menuconfig"
+a second time and configure it for a "Linux process":
+```
+make menuconfig
+```
+
+Then install this micro-controller code as well:
+```
+sudo service klipper stop
+make flash
+sudo service klipper start
+```
+
Remaining configuration
=======================
diff --git a/klippy/chipmisc.py b/klippy/chipmisc.py
index 7e875a4f..de4a5b9c 100644
--- a/klippy/chipmisc.py
+++ b/klippy/chipmisc.py
@@ -3,7 +3,7 @@
# Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-import pins
+import pins, mcu
######################################################################
@@ -58,10 +58,182 @@ class ad5206:
######################################################################
+# Replicape board
+######################################################################
+
+REPLICAPE_MAX_CURRENT = 3.84
+REPLICAPE_SHIFT_REGISTER_BUS = 1
+REPLICAPE_SHIFT_REGISTER_DEVICE = 1
+REPLICAPE_PCA9685_BUS = 2
+REPLICAPE_PCA9685_ADDRESS = 0x70
+REPLICAPE_PCA9685_CYCLE_TIME = .001
+
+class pca9685_pwm:
+ def __init__(self, replicape, channel, pin_params):
+ self._replicape = replicape
+ self._channel = channel
+ if pin_params['type'] not in ['digital_out', 'pwm']:
+ raise pins.error("Pin type not supported on replicape")
+ self._mcu = replicape.host_mcu
+ self._mcu.add_config_object(self)
+ self._bus = REPLICAPE_PCA9685_BUS
+ self._address = REPLICAPE_PCA9685_ADDRESS
+ self._cycle_time = REPLICAPE_PCA9685_CYCLE_TIME
+ self._max_duration = 2.
+ self._oid = None
+ self._invert = pin_params['invert']
+ self._last_clock = 0
+ self._pwm_max = 0.
+ self._cmd_queue = self._mcu.alloc_command_queue()
+ self._set_cmd = None
+ self._static_value = None
+ def get_mcu(self):
+ return self._mcu
+ def setup_max_duration(self, max_duration):
+ self._max_duration = max_duration
+ def setup_cycle_time(self, cycle_time):
+ pass
+ def setup_hard_pwm(self, hard_cycle_ticks):
+ if hard_cycle_ticks:
+ raise pins.error("pca9685 does not support hard_pwm parameter")
+ def setup_static_pwm(self, value):
+ if self._invert:
+ value = 1. - value
+ self._static_value = max(0., min(1., value))
+ def build_config(self):
+ self._pwm_max = self._mcu.get_constant_float("PCA9685_MAX")
+ cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
+ if self._static_value is not None:
+ value = int(self._static_value * self._pwm_max + 0.5)
+ self._mcu.add_config_cmd(
+ "set_pca9685_out bus=%d addr=%d channel=%d"
+ " cycle_ticks=%d value=%d" % (
+ self._bus, self._address, self._channel,
+ cycle_ticks, value))
+ return
+ self._oid = self._mcu.create_oid()
+ self._mcu.add_config_cmd(
+ "config_pca9685 oid=%d bus=%d addr=%d channel=%d"
+ " cycle_ticks=%d max_duration=%d" % (
+ self._oid, self._bus, self._address, self._channel,
+ cycle_ticks, self._mcu.seconds_to_clock(self._max_duration)))
+ self._set_cmd = self._mcu.lookup_command(
+ "schedule_pca9685_out oid=%c clock=%u value=%hu")
+ def set_pwm(self, print_time, value):
+ clock = self._mcu.print_time_to_clock(print_time)
+ if self._invert:
+ value = 1. - value
+ value = int(max(0., min(1., value)) * self._pwm_max + 0.5)
+ self._replicape.note_enable(print_time, self._channel, not not value)
+ msg = self._set_cmd.encode(self._oid, clock, value)
+ self._mcu.send(msg, minclock=self._last_clock, reqclock=clock
+ , cq=self._cmd_queue)
+ self._last_clock = clock
+ def set_digital(self, print_time, value):
+ if value:
+ self.set_pwm(print_time, 1.)
+ else:
+ self.set_pwm(print_time, 0.)
+
+class ReplicapeDACEnable:
+ def __init__(self, replicape, channel, pin_params):
+ if pin_params['type'] != 'digital_out':
+ raise pins.error("Replicape virtual enable pin must be digital_out")
+ if pin_params['invert']:
+ raise pins.error("Replicape virtual enable pin can not be inverted")
+ self.mcu = replicape.host_mcu
+ self.value = replicape.stepper_dacs[channel]
+ self.pwm = pca9685_pwm(replicape, channel, pin_params)
+ self.last = 0
+ def get_mcu(self):
+ return self.mcu
+ def setup_max_duration(self, max_duration):
+ self.pwm.setup_max_duration(max_duration)
+ def set_digital(self, print_time, value):
+ if value:
+ self.pwm.set_pwm(print_time, self.value)
+ else:
+ self.pwm.set_pwm(print_time, 0.)
+ self.last = value
+ def get_last_setting(self):
+ return self.last
+
+ReplicapeStepConfig = {
+ 'disable': None,
+ '1': (1<<7)|(1<<5), '2': (1<<7)|(1<<5)|(1<<6), 'spread2': (1<<5),
+ '4': (1<<7)|(1<<5)|(1<<4), '16': (1<<7)|(1<<5)|(1<<6)|(1<<4),
+ 'spread4': (1<<5)|(1<<4), 'spread16': (1<<7), 'stealth4': (1<<7)|(1<<6),
+ 'stealth16': 0
+}
+
+class Replicape:
+ def __init__(self, printer, config):
+ pins.get_printer_pins(printer).register_chip('replicape', self)
+ revisions = {'B3': 'B3'}
+ config.getchoice('revision', revisions)
+ self.host_mcu = mcu.get_printer_mcu(printer, config.get('host_mcu'))
+ # Setup enable pin
+ self.mcu_enable = pins.setup_pin(
+ printer, 'digital_out', config.get('enable_pin', '!P9_41'))
+ self.mcu_enable.setup_max_duration(0.)
+ self.enabled_channels = {}
+ # Setup power pins
+ self.pins = {
+ "power_e": (pca9685_pwm, 5), "power_h": (pca9685_pwm, 3),
+ "power_hotbed": (pca9685_pwm, 4),
+ "power_fan0": (pca9685_pwm, 7), "power_fan1": (pca9685_pwm, 8),
+ "power_fan2": (pca9685_pwm, 9), "power_fan3": (pca9685_pwm, 10) }
+ # Setup stepper config
+ self.stepper_dacs = {}
+ shift_registers = [1] * 5
+ for port, name in enumerate('xyzeh'):
+ prefix = 'stepper_%s_' % (name,)
+ sc = config.getchoice(
+ prefix + 'microstep_mode', ReplicapeStepConfig, 'disable')
+ if sc is None:
+ continue
+ if config.getboolean(prefix + 'chopper_off_time_high', False):
+ sc |= 1<<3
+ if config.getboolean(prefix + 'chopper_hysteresis_high', False):
+ sc |= 1<<2
+ if config.getboolean(prefix + 'chopper_blank_time_high', True):
+ sc |= 1<<1
+ shift_registers[port] = sc
+ channel = port + 11
+ cur = config.getfloat(
+ prefix + 'current', above=0., maxval=REPLICAPE_MAX_CURRENT)
+ self.stepper_dacs[channel] = cur / REPLICAPE_MAX_CURRENT
+ self.pins[prefix + 'enable'] = (ReplicapeDACEnable, channel)
+ shift_registers.reverse()
+ self.host_mcu.add_config_cmd("send_spi bus=%d dev=%d msg=%s" % (
+ REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
+ "".join(["%02x" % (x,) for x in shift_registers])))
+ def note_enable(self, print_time, channel, is_enable):
+ if is_enable:
+ is_off = not self.enabled_channels
+ self.enabled_channels[channel] = 1
+ if is_off:
+ self.mcu_enable.set_digital(print_time, 1)
+ elif channel in self.enabled_channels:
+ del self.enabled_channels[channel]
+ if not self.enabled_channels:
+ self.mcu_enable.set_digital(print_time, 0)
+ def setup_pin(self, pin_params):
+ pin = pin_params['pin']
+ if pin not in self.pins:
+ raise pins.error("Unknown replicape pin %s" % (pin,))
+ pclass, channel = self.pins[pin]
+ return pclass(self, channel, pin_params)
+
+
+######################################################################
# Setup
######################################################################
def add_printer_objects(printer, config):
+ if config.has_section('replicape'):
+ printer.add_object('replicape', Replicape(
+ printer, config.getsection('replicape')))
for s in config.get_prefix_sections('static_digital_output '):
printer.add_object(s.section, PrinterStaticDigitalOut(printer, s))
for s in config.get_prefix_sections('static_pwm_output '):