From 978777f09fa494db1b50fba58ea4e57d9b8479ac Mon Sep 17 00:00:00 2001
From: Kevin O'Connor <kevin@koconnor.net>
Date: Sat, 20 Jan 2018 00:18:28 -0500
Subject: [PATCH] replicape: Move the replicape code from chipmisc.py to extras
 directory

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
---
 config/generic-replicape.cfg | 128 ++++++++++----------
 klippy/chipmisc.py           | 218 +---------------------------------
 klippy/extras/replicape.py   | 221 +++++++++++++++++++++++++++++++++++
 3 files changed, 286 insertions(+), 281 deletions(-)
 create mode 100644 klippy/extras/replicape.py

diff --git a/config/generic-replicape.cfg b/config/generic-replicape.cfg
index d9694beb1..de2761ab4 100644
--- a/config/generic-replicape.cfg
+++ b/config/generic-replicape.cfg
@@ -13,74 +13,10 @@
 
 # See the example.cfg file for a description of available parameters.
 
-[stepper_x]
-step_pin: P8_17
-dir_pin: P8_26
-enable_pin: replicape:stepper_x_enable
-step_distance: .0125
-endstop_pin: ^P9_25
-position_endstop: 0
-position_max: 200
-homing_speed: 50
-
-[stepper_y]
-step_pin: P8_12
-dir_pin: P8_19
-enable_pin: replicape:stepper_y_enable
-step_distance: .0125
-endstop_pin: ^P9_23
-position_endstop: 0
-position_max: 200
-homing_speed: 50
-
-[stepper_z]
-step_pin: P8_13
-dir_pin: P8_14
-enable_pin: replicape:stepper_z_enable
-step_distance: 0.00025
-endstop_pin: ^P9_13
-position_endstop: 0
-position_max: 200
-
-[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
-
-[heater_bed]
-heater_pin: replicape:power_hotbed
-sensor_type: EPCOS 100K B57560G104F
-sensor_pin: host:analog6
-control: watermark
-min_temp: 0
-max_temp: 130
-
-[fan]
-pin: replicape:power_fan0
-
 [mcu]
 serial: /dev/rpmsg_pru30
 pin_map: beaglebone
 
-[printer]
-kinematics: cartesian
-max_velocity: 300
-max_accel: 3000
-max_z_velocity: 25
-max_z_accel: 30
-
 [mcu host]
 serial: /tmp/klipper_host_mcu
 
@@ -126,3 +62,67 @@ stepper_z_microstep_mode: spread16
 stepper_z_current: 0.5
 stepper_e_microstep_mode: 16
 stepper_e_current: 0.5
+
+[stepper_x]
+step_pin: P8_17
+dir_pin: P8_26
+enable_pin: replicape:stepper_x_enable
+step_distance: .0125
+endstop_pin: ^P9_25
+position_endstop: 0
+position_max: 200
+homing_speed: 50
+
+[stepper_y]
+step_pin: P8_12
+dir_pin: P8_19
+enable_pin: replicape:stepper_y_enable
+step_distance: .0125
+endstop_pin: ^P9_23
+position_endstop: 0
+position_max: 200
+homing_speed: 50
+
+[stepper_z]
+step_pin: P8_13
+dir_pin: P8_14
+enable_pin: replicape:stepper_z_enable
+step_distance: 0.00025
+endstop_pin: ^P9_13
+position_endstop: 0
+position_max: 200
+
+[printer]
+kinematics: cartesian
+max_velocity: 300
+max_accel: 3000
+max_z_velocity: 25
+max_z_accel: 30
+
+[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
+
+[heater_bed]
+heater_pin: replicape:power_hotbed
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: host:analog6
+control: watermark
+min_temp: 0
+max_temp: 130
+
+[fan]
+pin: replicape:power_fan0
diff --git a/klippy/chipmisc.py b/klippy/chipmisc.py
index 08b4bf0b3..611823eca 100644
--- a/klippy/chipmisc.py
+++ b/klippy/chipmisc.py
@@ -3,7 +3,7 @@
 # Copyright (C) 2017,2018  Kevin O'Connor <kevin@koconnor.net>
 #
 # This file may be distributed under the terms of the GNU GPLv3 license.
-import pins, mcu
+import pins
 
 
 ######################################################################
@@ -78,227 +78,11 @@ class PrinterPin:
         self.last_value_time = print_time
 
 
-######################################################################
-# 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._start_value = self._shutdown_value = float(self._invert)
-        self._is_static = False
-        self._last_clock = 0
-        self._pwm_max = 0.
-        self._cmd_queue = self._mcu.alloc_command_queue()
-        self._set_cmd = 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_start_value(self, start_value, shutdown_value, is_static=False):
-        if is_static and start_value != shutdown_value:
-            raise pins.error("Static pin can not have shutdown value")
-        if self._invert:
-            start_value = 1. - start_value
-            shutdown_value = 1. - shutdown_value
-        self._start_value = max(0., min(1., start_value))
-        self._shutdown_value = max(0., min(1., shutdown_value))
-        self._is_static = is_static
-        self._replicape.note_pwm_start_value(
-            self._channel, self._start_value, self._shutdown_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._is_static:
-            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, self._start_value * self._pwm_max))
-            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"
-            " value=%d default_value=%d max_duration=%d" % (
-                self._oid, self._bus, self._address, self._channel, cycle_ticks,
-                self._start_value * self._pwm_max,
-                self._shutdown_value * self._pwm_max,
-                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_pwm_enable(print_time, self._channel, 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)
-    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.)
-
-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_pwm_enable = pins.setup_pin(
-            printer, 'digital_out', config.get('enable_pin', '!P9_41'))
-        self.mcu_pwm_enable.setup_max_duration(0.)
-        self.mcu_pwm_start_value = self.mcu_pwm_shutdown_value = False
-        # 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, 0, 0, 1, 1]
-        for port, name in enumerate('xyzeh'):
-            prefix = 'stepper_%s_' % (name,)
-            sc = config.getchoice(
-                prefix + 'microstep_mode', ReplicapeStepConfig, 'disable')
-            if sc is None:
-                continue
-            sc |= shift_registers[port]
-            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)
-        self.enabled_channels = {ch: False for cl, ch in self.pins.values()}
-        self.disable_stepper_cmd = "send_spi bus=%d dev=%d msg=%s" % (
-            REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
-            "".join(["%02x" % (x,) for x in reversed(shift_registers)]))
-        if [i for i in [0, 1, 2] if 11+i in self.stepper_dacs]:
-            # Enable xyz steppers
-            shift_registers[0] &= ~1
-        if [i for i in [3, 4] if 11+i in self.stepper_dacs]:
-            # Enable eh steppers
-            shift_registers[3] &= ~1
-        if (config.getboolean('standstill_power_down', False)
-            and self.stepper_dacs):
-            shift_registers[4] &= ~1
-        self.enable_stepper_cmd = "send_spi bus=%d dev=%d msg=%s" % (
-            REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
-            "".join(["%02x" % (x,) for x in reversed(shift_registers)]))
-        self.host_mcu.add_config_cmd(self.disable_stepper_cmd)
-        self.last_stepper_time = 0.
-    def note_pwm_start_value(self, channel, start_value, shutdown_value):
-        self.mcu_pwm_start_value |= not not start_value
-        self.mcu_pwm_shutdown_value |= not not shutdown_value
-        self.mcu_pwm_enable.setup_start_value(
-            self.mcu_pwm_start_value, self.mcu_pwm_shutdown_value)
-        self.enabled_channels[channel] = not not start_value
-    def note_pwm_enable(self, print_time, channel, value):
-        is_enable = not not value
-        if self.enabled_channels[channel] == is_enable:
-            # Nothing to do
-            return
-        self.enabled_channels[channel] = is_enable
-        # Check if need to set the pca9685 enable pin
-        on_channels = [1 for c, e in self.enabled_channels.items() if e]
-        if not on_channels:
-            self.mcu_pwm_enable.set_digital(print_time, 0)
-        elif is_enable and len(on_channels) == 1:
-            self.mcu_pwm_enable.set_digital(print_time, 1)
-        # Check if need to set the stepper enable lines
-        if channel not in self.stepper_dacs:
-            return
-        on_dacs = [1 for c in self.stepper_dacs.keys()
-                   if self.enabled_channels[c]]
-        if not on_dacs:
-            cmd = self.disable_stepper_cmd
-        elif is_enable and len(on_dacs) == 1:
-            cmd = self.enable_stepper_cmd
-        else:
-            return
-        print_time = max(print_time, self.last_stepper_time + PIN_MIN_TIME)
-        clock = self.host_mcu.print_time_to_clock(print_time)
-        # XXX - the send_spi message should be scheduled
-        self.host_mcu.send(self.host_mcu.create_command(cmd),
-                           minclock=clock, reqclock=clock)
-    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('digital_output '):
diff --git a/klippy/extras/replicape.py b/klippy/extras/replicape.py
new file mode 100644
index 000000000..72ab45e9c
--- /dev/null
+++ b/klippy/extras/replicape.py
@@ -0,0 +1,221 @@
+# Code to configure miscellaneous chips
+#
+# Copyright (C) 2017,2018  Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+import pins, mcu
+
+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
+PIN_MIN_TIME = 0.100
+
+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._start_value = self._shutdown_value = float(self._invert)
+        self._is_static = False
+        self._last_clock = 0
+        self._pwm_max = 0.
+        self._cmd_queue = self._mcu.alloc_command_queue()
+        self._set_cmd = 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_start_value(self, start_value, shutdown_value, is_static=False):
+        if is_static and start_value != shutdown_value:
+            raise pins.error("Static pin can not have shutdown value")
+        if self._invert:
+            start_value = 1. - start_value
+            shutdown_value = 1. - shutdown_value
+        self._start_value = max(0., min(1., start_value))
+        self._shutdown_value = max(0., min(1., shutdown_value))
+        self._is_static = is_static
+        self._replicape.note_pwm_start_value(
+            self._channel, self._start_value, self._shutdown_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._is_static:
+            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, self._start_value * self._pwm_max))
+            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"
+            " value=%d default_value=%d max_duration=%d" % (
+                self._oid, self._bus, self._address, self._channel, cycle_ticks,
+                self._start_value * self._pwm_max,
+                self._shutdown_value * self._pwm_max,
+                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_pwm_enable(print_time, self._channel, 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)
+    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.)
+
+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, config):
+        printer = config.get_printer()
+        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_pwm_enable = pins.setup_pin(
+            printer, 'digital_out', config.get('enable_pin', '!P9_41'))
+        self.mcu_pwm_enable.setup_max_duration(0.)
+        self.mcu_pwm_start_value = self.mcu_pwm_shutdown_value = False
+        # 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, 0, 0, 1, 1]
+        for port, name in enumerate('xyzeh'):
+            prefix = 'stepper_%s_' % (name,)
+            sc = config.getchoice(
+                prefix + 'microstep_mode', ReplicapeStepConfig, 'disable')
+            if sc is None:
+                continue
+            sc |= shift_registers[port]
+            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)
+        self.enabled_channels = {ch: False for cl, ch in self.pins.values()}
+        self.disable_stepper_cmd = "send_spi bus=%d dev=%d msg=%s" % (
+            REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
+            "".join(["%02x" % (x,) for x in reversed(shift_registers)]))
+        if [i for i in [0, 1, 2] if 11+i in self.stepper_dacs]:
+            # Enable xyz steppers
+            shift_registers[0] &= ~1
+        if [i for i in [3, 4] if 11+i in self.stepper_dacs]:
+            # Enable eh steppers
+            shift_registers[3] &= ~1
+        if (config.getboolean('standstill_power_down', False)
+            and self.stepper_dacs):
+            shift_registers[4] &= ~1
+        self.enable_stepper_cmd = "send_spi bus=%d dev=%d msg=%s" % (
+            REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
+            "".join(["%02x" % (x,) for x in reversed(shift_registers)]))
+        self.host_mcu.add_config_cmd(self.disable_stepper_cmd)
+        self.last_stepper_time = 0.
+    def note_pwm_start_value(self, channel, start_value, shutdown_value):
+        self.mcu_pwm_start_value |= not not start_value
+        self.mcu_pwm_shutdown_value |= not not shutdown_value
+        self.mcu_pwm_enable.setup_start_value(
+            self.mcu_pwm_start_value, self.mcu_pwm_shutdown_value)
+        self.enabled_channels[channel] = not not start_value
+    def note_pwm_enable(self, print_time, channel, value):
+        is_enable = not not value
+        if self.enabled_channels[channel] == is_enable:
+            # Nothing to do
+            return
+        self.enabled_channels[channel] = is_enable
+        # Check if need to set the pca9685 enable pin
+        on_channels = [1 for c, e in self.enabled_channels.items() if e]
+        if not on_channels:
+            self.mcu_pwm_enable.set_digital(print_time, 0)
+        elif is_enable and len(on_channels) == 1:
+            self.mcu_pwm_enable.set_digital(print_time, 1)
+        # Check if need to set the stepper enable lines
+        if channel not in self.stepper_dacs:
+            return
+        on_dacs = [1 for c in self.stepper_dacs.keys()
+                   if self.enabled_channels[c]]
+        if not on_dacs:
+            cmd = self.disable_stepper_cmd
+        elif is_enable and len(on_dacs) == 1:
+            cmd = self.enable_stepper_cmd
+        else:
+            return
+        print_time = max(print_time, self.last_stepper_time + PIN_MIN_TIME)
+        clock = self.host_mcu.print_time_to_clock(print_time)
+        # XXX - the send_spi message should be scheduled
+        self.host_mcu.send(self.host_mcu.create_command(cmd),
+                           minclock=clock, reqclock=clock)
+    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)
+
+def load_config(config):
+    if config.get_name() != 'replicape':
+        raise config.error("Invalid replicape config name")
+    return Replicape(config)