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>
This commit is contained in:
Kevin O'Connor 2017-08-25 23:31:58 -04:00
parent 5a85c1667a
commit 3b4088c23f
4 changed files with 269 additions and 38 deletions

View File

@ -122,3 +122,8 @@
# its highest resistance, and then the 'channel_x' parameters can be # its highest resistance, and then the 'channel_x' parameters can be
# specified using the desired amperage value for the stepper. The # specified using the desired amperage value for the stepper. The
# default is to not scale the 'channel_x' parameters. # default is to not scale the 'channel_x' parameters.
# Replicape support - see the generic-replicape.cfg file for further
# details.
#[replicape]

View File

@ -1,14 +1,15 @@
# This file contains an example configuration for the Beaglebone PRU # This file contains an example configuration for the Replicape rev B3
# micro-controller. # board. To use this config, one must compile and install the
# micro-controller code for the "Beaglebone PRU", and then compile and
# THIS FILE HAS NOT BEEN TESTED - PROCEED WITH CAUTION! # install the micro-controller code a second time for a "Linux
# process".
# See the example.cfg file for a description of available parameters. # See the example.cfg file for a description of available parameters.
[stepper_x] [stepper_x]
step_pin: P8_17 step_pin: P8_17
dir_pin: P8_26 dir_pin: P8_26
enable_pin: !P9_41 enable_pin: replicape:stepper_x_enable
step_distance: .0125 step_distance: .0125
endstop_pin: ^P9_25 endstop_pin: ^P9_25
position_endstop: 0 position_endstop: 0
@ -18,7 +19,7 @@ homing_speed: 50
[stepper_y] [stepper_y]
step_pin: P8_12 step_pin: P8_12
dir_pin: P8_19 dir_pin: P8_19
enable_pin: !P9_41 enable_pin: replicape:stepper_y_enable
step_distance: .0125 step_distance: .0125
endstop_pin: ^P9_23 endstop_pin: ^P9_23
position_endstop: 0 position_endstop: 0
@ -28,42 +29,39 @@ homing_speed: 50
[stepper_z] [stepper_z]
step_pin: P8_13 step_pin: P8_13
dir_pin: P8_14 dir_pin: P8_14
enable_pin: !P9_41 enable_pin: replicape:stepper_z_enable
step_distance: 0.00025 step_distance: 0.00025
endstop_pin: ^P9_13 endstop_pin: ^P9_13
position_endstop: 0 position_endstop: 0
position_max: 200 position_max: 200
# XXX - Extruder heater hooked up to i2c mosfet [extruder]
#[extruder] step_pin: P9_12
#step_pin: P9_12 dir_pin: P8_15
#dir_pin: P8_15 enable_pin: replicape:stepper_e_enable
#enable_pin: !P9_41 step_distance: .002
#step_distance: .002 nozzle_diameter: 0.400
#nozzle_diameter: 0.400 filament_diameter: 1.750
#filament_diameter: 1.750 heater_pin: replicape:power_e
#heater_pin: ? sensor_type: EPCOS 100K B57560G104F
#sensor_type: EPCOS 100K B57560G104F sensor_pin: host:analog4
#sensor_pin: P9_39 control: pid
#control: pid pid_Kp: 22.2
#pid_Kp: 22.2 pid_Ki: 1.08
#pid_Ki: 1.08 pid_Kd: 114
#pid_Kd: 114 min_temp: 0
#min_temp: 0 max_temp: 250
#max_temp: 250
# XXX - Bed heater hooked up to i2c mosfet [heater_bed]
#[heater_bed] heater_pin: replicape:power_hotbed
#heater_pin: ? sensor_type: EPCOS 100K B57560G104F
#sensor_type: EPCOS 100K B57560G104F sensor_pin: host:analog6
#sensor_pin: P9_40 control: watermark
#control: watermark min_temp: 0
#min_temp: 0 max_temp: 130
#max_temp: 130
# XXX - Fan power hooked up to i2c mosfet [fan]
#[fan] pin: replicape:power_fan0
#pin: ?
[mcu] [mcu]
serial: /dev/rpmsg_pru30 serial: /dev/rpmsg_pru30
@ -73,5 +71,47 @@ pin_map: beaglebone
kinematics: cartesian kinematics: cartesian
max_velocity: 300 max_velocity: 300
max_accel: 3000 max_accel: 3000
max_z_velocity: 5 max_z_velocity: 25
max_z_accel: 100 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

View File

@ -71,6 +71,20 @@ make flash
sudo service klipper start 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 Remaining configuration
======================= =======================

View File

@ -3,7 +3,7 @@
# Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net> # Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
# #
# This file may be distributed under the terms of the GNU GPLv3 license. # This file may be distributed under the terms of the GNU GPLv3 license.
import pins import pins, mcu
###################################################################### ######################################################################
@ -57,11 +57,183 @@ class ad5206:
"send_spi_message pin=%s msg=%02x%02x" % (self.pin, i, val)) "send_spi_message pin=%s msg=%02x%02x" % (self.pin, i, val))
######################################################################
# 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 # Setup
###################################################################### ######################################################################
def add_printer_objects(printer, config): 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 '): for s in config.get_prefix_sections('static_digital_output '):
printer.add_object(s.section, PrinterStaticDigitalOut(printer, s)) printer.add_object(s.section, PrinterStaticDigitalOut(printer, s))
for s in config.get_prefix_sections('static_pwm_output '): for s in config.get_prefix_sections('static_pwm_output '):