2021-10-25 03:22:36 +02:00
|
|
|
# Pin name handling
|
2016-05-25 17:37:40 +02:00
|
|
|
#
|
2021-10-25 03:22:36 +02:00
|
|
|
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
|
2016-05-25 17:37:40 +02:00
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
import re
|
|
|
|
|
2018-09-04 00:13:15 +02:00
|
|
|
class error(Exception):
|
|
|
|
pass
|
|
|
|
|
2017-08-21 17:25:26 +02:00
|
|
|
|
2017-03-13 04:05:01 +01:00
|
|
|
######################################################################
|
2017-08-21 17:25:26 +02:00
|
|
|
# Command translation
|
2017-03-13 04:05:01 +01:00
|
|
|
######################################################################
|
|
|
|
|
|
|
|
re_pin = re.compile(r'(?P<prefix>[ _]pin=)(?P<name>[^ ]*)')
|
2018-01-11 00:34:42 +01:00
|
|
|
|
|
|
|
class PinResolver:
|
2019-08-09 17:42:50 +02:00
|
|
|
def __init__(self, validate_aliases=True):
|
2018-01-11 00:34:42 +01:00
|
|
|
self.validate_aliases = validate_aliases
|
2019-08-09 17:42:50 +02:00
|
|
|
self.reserved = {}
|
2019-03-12 17:47:07 +01:00
|
|
|
self.aliases = {}
|
2018-01-11 00:34:42 +01:00
|
|
|
self.active_pins = {}
|
2019-08-09 17:42:50 +02:00
|
|
|
def reserve_pin(self, pin, reserve_name):
|
|
|
|
if pin in self.reserved and self.reserved[pin] != reserve_name:
|
|
|
|
raise error("Pin %s reserved for %s - can't reserve for %s" % (
|
|
|
|
pin, self.reserved[pin], reserve_name))
|
|
|
|
self.reserved[pin] = reserve_name
|
2019-08-09 17:53:09 +02:00
|
|
|
def alias_pin(self, alias, pin):
|
|
|
|
if alias in self.aliases and self.aliases[alias] != pin:
|
|
|
|
raise error("Alias %s mapped to %s - can't alias to %s" % (
|
|
|
|
alias, self.aliases[alias], pin))
|
2022-05-09 22:33:46 +02:00
|
|
|
if [c for c in '^~!:' if c in pin] or ''.join(pin.split()) != pin:
|
|
|
|
raise error("Invalid pin alias '%s'\n" % (pin,))
|
2019-08-09 17:53:09 +02:00
|
|
|
if pin in self.aliases:
|
|
|
|
pin = self.aliases[pin]
|
|
|
|
self.aliases[alias] = pin
|
|
|
|
for existing_alias, existing_pin in self.aliases.items():
|
|
|
|
if existing_pin == alias:
|
|
|
|
self.aliases[existing_alias] = pin
|
2018-01-11 00:34:42 +01:00
|
|
|
def update_command(self, cmd):
|
|
|
|
def pin_fixup(m):
|
|
|
|
name = m.group('name')
|
2019-03-12 17:47:07 +01:00
|
|
|
pin_id = self.aliases.get(name, name)
|
2018-01-11 00:34:42 +01:00
|
|
|
if (name != self.active_pins.setdefault(pin_id, name)
|
|
|
|
and self.validate_aliases):
|
|
|
|
raise error("pin %s is an alias for %s" % (
|
|
|
|
name, self.active_pins[pin_id]))
|
2019-08-09 17:42:50 +02:00
|
|
|
if pin_id in self.reserved:
|
2019-04-01 02:30:14 +02:00
|
|
|
raise error("pin %s is reserved for %s" % (
|
2019-08-09 17:42:50 +02:00
|
|
|
name, self.reserved[pin_id]))
|
2018-01-11 00:34:42 +01:00
|
|
|
return m.group('prefix') + str(pin_id)
|
|
|
|
return re_pin.sub(pin_fixup, cmd)
|
2017-08-21 17:25:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Pin to chip mapping
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
class PrinterPins:
|
|
|
|
error = error
|
|
|
|
def __init__(self):
|
|
|
|
self.chips = {}
|
2018-01-10 20:49:00 +01:00
|
|
|
self.active_pins = {}
|
2019-08-09 17:42:50 +02:00
|
|
|
self.pin_resolvers = {}
|
2021-06-14 04:52:57 +02:00
|
|
|
self.allow_multi_use_pins = {}
|
2019-06-11 01:51:43 +02:00
|
|
|
def parse_pin(self, pin_desc, can_invert=False, can_pullup=False):
|
2018-06-30 20:05:27 +02:00
|
|
|
desc = pin_desc.strip()
|
2017-08-21 17:25:26 +02:00
|
|
|
pullup = invert = 0
|
2019-02-27 17:07:51 +01:00
|
|
|
if can_pullup and (desc.startswith('^') or desc.startswith('~')):
|
2017-08-21 17:25:26 +02:00
|
|
|
pullup = 1
|
2019-02-27 17:07:51 +01:00
|
|
|
if desc.startswith('~'):
|
|
|
|
pullup = -1
|
2018-02-07 21:56:38 +01:00
|
|
|
desc = desc[1:].strip()
|
|
|
|
if can_invert and desc.startswith('!'):
|
2017-08-21 17:25:26 +02:00
|
|
|
invert = 1
|
2018-02-07 21:56:38 +01:00
|
|
|
desc = desc[1:].strip()
|
|
|
|
if ':' not in desc:
|
|
|
|
chip_name, pin = 'mcu', desc
|
2017-08-21 17:25:26 +02:00
|
|
|
else:
|
2018-02-07 21:56:38 +01:00
|
|
|
chip_name, pin = [s.strip() for s in desc.split(':', 1)]
|
2017-08-21 17:25:26 +02:00
|
|
|
if chip_name not in self.chips:
|
|
|
|
raise error("Unknown pin chip name '%s'" % (chip_name,))
|
2022-05-09 22:33:46 +02:00
|
|
|
if [c for c in '^~!:' if c in pin] or ''.join(pin.split()) != pin:
|
2018-02-07 21:56:38 +01:00
|
|
|
format = ""
|
|
|
|
if can_pullup:
|
2019-03-18 02:07:31 +01:00
|
|
|
format += "[^~] "
|
2018-02-07 21:56:38 +01:00
|
|
|
if can_invert:
|
|
|
|
format += "[!] "
|
|
|
|
raise error("Invalid pin description '%s'\n"
|
|
|
|
"Format is: %s[chip_name:] pin_name" % (
|
|
|
|
pin_desc, format))
|
2019-06-11 01:51:43 +02:00
|
|
|
pin_params = {'chip': self.chips[chip_name], 'chip_name': chip_name,
|
|
|
|
'pin': pin, 'invert': invert, 'pullup': pullup}
|
|
|
|
return pin_params
|
|
|
|
def lookup_pin(self, pin_desc, can_invert=False, can_pullup=False,
|
|
|
|
share_type=None):
|
|
|
|
pin_params = self.parse_pin(pin_desc, can_invert, can_pullup)
|
|
|
|
pin = pin_params['pin']
|
|
|
|
share_name = "%s:%s" % (pin_params['chip_name'], pin)
|
2018-01-10 20:49:00 +01:00
|
|
|
if share_name in self.active_pins:
|
2019-06-11 01:51:43 +02:00
|
|
|
share_params = self.active_pins[share_name]
|
2021-06-14 04:52:57 +02:00
|
|
|
if share_name in self.allow_multi_use_pins:
|
|
|
|
pass
|
|
|
|
elif share_type is None or share_type != share_params['share_type']:
|
2018-01-10 20:49:00 +01:00
|
|
|
raise error("pin %s used multiple times in config" % (pin,))
|
2021-06-14 04:52:57 +02:00
|
|
|
elif (pin_params['invert'] != share_params['invert']
|
|
|
|
or pin_params['pullup'] != share_params['pullup']):
|
2018-01-10 20:49:00 +01:00
|
|
|
raise error("Shared pin %s must have same polarity" % (pin,))
|
2019-06-11 01:51:43 +02:00
|
|
|
return share_params
|
|
|
|
pin_params['share_type'] = share_type
|
2018-01-10 20:49:00 +01:00
|
|
|
self.active_pins[share_name] = pin_params
|
|
|
|
return pin_params
|
2018-01-10 20:21:25 +01:00
|
|
|
def setup_pin(self, pin_type, pin_desc):
|
2019-11-12 19:55:50 +01:00
|
|
|
can_invert = pin_type in ['endstop', 'digital_out', 'pwm']
|
2018-07-26 15:44:45 +02:00
|
|
|
can_pullup = pin_type in ['endstop']
|
|
|
|
pin_params = self.lookup_pin(pin_desc, can_invert, can_pullup)
|
|
|
|
return pin_params['chip'].setup_pin(pin_type, pin_params)
|
2018-12-31 00:12:07 +01:00
|
|
|
def reset_pin_sharing(self, pin_params):
|
|
|
|
share_name = "%s:%s" % (pin_params['chip_name'], pin_params['pin'])
|
|
|
|
del self.active_pins[share_name]
|
2019-08-09 17:42:50 +02:00
|
|
|
def get_pin_resolver(self, chip_name):
|
|
|
|
if chip_name not in self.pin_resolvers:
|
|
|
|
raise error("Unknown chip name '%s'" % (chip_name,))
|
|
|
|
return self.pin_resolvers[chip_name]
|
2017-08-21 17:25:26 +02:00
|
|
|
def register_chip(self, chip_name, chip):
|
|
|
|
chip_name = chip_name.strip()
|
|
|
|
if chip_name in self.chips:
|
|
|
|
raise error("Duplicate chip name '%s'" % (chip_name,))
|
|
|
|
self.chips[chip_name] = chip
|
2019-08-09 17:42:50 +02:00
|
|
|
self.pin_resolvers[chip_name] = PinResolver()
|
2021-06-14 04:52:57 +02:00
|
|
|
def allow_multi_use_pin(self, pin_desc):
|
|
|
|
pin_params = self.parse_pin(pin_desc)
|
|
|
|
share_name = "%s:%s" % (pin_params['chip_name'], pin_params['pin'])
|
|
|
|
self.allow_multi_use_pins[share_name] = True
|
2017-08-21 17:25:26 +02:00
|
|
|
|
2018-07-13 04:26:32 +02:00
|
|
|
def add_printer_objects(config):
|
|
|
|
config.get_printer().add_object('pins', PrinterPins())
|