Skip to content

Commit bd2e26e

Browse files
committed
[api] Move GPIO code from modm to here
1 parent 915724f commit bd2e26e

File tree

8 files changed

+267
-2
lines changed

8 files changed

+267
-2
lines changed

tools/device/modm_devices/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@
1010
from . import device_identifier
1111
from . import device
1212
from . import parser
13+
from . import stm32
1314

1415
from .pkg import naturalkey
1516
from .exception import ParserException
1617

17-
__all__ = ['exception', 'device_file', 'device_identifier', 'device', 'parser', 'pkg']
18+
__all__ = [
19+
'exception',
20+
'device_file',
21+
'device_identifier',
22+
'device',
23+
'parser',
24+
'pkg'
25+
]
1826

1927
__author__ = "Fabian Greif"
2028
__copyright__ = "Fabian Greif"

tools/device/modm_devices/cache.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
import functools
5+
6+
class cached_property(object):
7+
def __init__(self, func):
8+
self.__doc__ = getattr(func, "__doc__")
9+
self.func = func
10+
11+
def __get__(self, obj, cls):
12+
if obj is None:
13+
return self
14+
value = obj.__dict__[self.func.__name__] = self.func(obj)
15+
return value
16+
17+
cached_function = functools.lru_cache(None)

tools/device/modm_devices/device_file.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@
1111
from collections import defaultdict
1212

1313
from .device import Device
14+
from .stm32.device import Stm32Device
1415
from .device_identifier import DeviceIdentifier
1516
from .device_identifier import MultiDeviceIdentifier
1617
from .access import read_only
1718

1819
from .exception import ParserException
1920

21+
def build_device(did, device_file):
22+
if did.platform == "stm32":
23+
from .stm32.device import Stm32Device
24+
return Stm32Device(did, device_file)
25+
return Device(did, device_file)
26+
2027
class DeviceFile:
2128
_PREFIX_ATTRIBUTE = 'attribute-'
2229
_PREFIX_ATTRIBUTE_DEVICE = 'device-'
@@ -50,7 +57,7 @@ def get_devices(self):
5057
devices = [did for did in devices if did.string not in invalid_devices]
5158
if len(valid_devices):
5259
devices = [did for did in devices if did.string in valid_devices]
53-
return [Device(did, self) for did in devices]
60+
return [build_device(did, self) for did in devices]
5461

5562
@staticmethod
5663
def is_valid(node, identifier: DeviceIdentifier):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import device
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2020, Niklas Hauser
5+
# All rights reserved.
6+
7+
class DriverAdc:
8+
def __init__(self, device):
9+
self._device = device
10+
self._adc = self._device.get_driver("adc")
11+
12+
def instances(self, default=None):
13+
"""
14+
:return: a map from int(pin number) to str(name) EXTI{name} interrupt.
15+
"""
16+
if "instance" in self._adc:
17+
return list(map(int, self._adc["instance"]))
18+
return default
19+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2016, Fabian Greif
5+
# Copyright (c) 2016, Niklas Hauser
6+
# All rights reserved.
7+
8+
from .gpio import DriverGpio
9+
from .exti import DriverExti
10+
from .adc import DriverAdc
11+
from ..device import Device
12+
from ..cache import cached_property
13+
from ..access import copy_keys
14+
15+
16+
class Stm32Device(Device):
17+
def __init__(self, identifier, device_file):
18+
Device.__init__(self, identifier, device_file)
19+
20+
@cached_property
21+
def gpio(self):
22+
return DriverGpio(self)
23+
24+
@cached_property
25+
def exti(self):
26+
return DriverExti(self)
27+
28+
@cached_property
29+
def adc(self):
30+
return DriverAdc(self)
31+
32+
@cached_property
33+
def peripherals(self):
34+
all_peripherals = []
35+
for s in self.gpio.signals_all:
36+
d = copy_keys(s, "driver", "instance")
37+
if len(d): all_peripherals.append(d);
38+
39+
# Signals are not enough, since there are peripherals that don't have signals.
40+
# Example: STM32F401RE < 64pins: SPI4 cannot be connected to any pins.
41+
for d in self._properties["driver"]:
42+
driver = d["name"]
43+
if driver in ["gpio", "core"]:
44+
continue
45+
elif "instance" in d:
46+
all_peripherals.extend( {"driver": driver, "instance": int(i)} for i in d["instance"] )
47+
else:
48+
all_peripherals.append( {"driver": driver} )
49+
50+
for r in self.gpio._gpio.get("remap", {}):
51+
d = copy_keys(r, "driver", "instance")
52+
if len(d): all_peripherals.append(d);
53+
54+
return all_peripherals
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2020, Niklas Hauser
5+
# All rights reserved.
6+
7+
from ..cache import cached_property
8+
9+
class DriverExti:
10+
def __init__(self, device):
11+
self._device = device
12+
self._core = self._device.get_driver("core")
13+
14+
@cached_property
15+
def vector_map(self):
16+
"""
17+
:return: a map from int(pin number) to str(name) EXTI{name} interrupt.
18+
"""
19+
extimap = {}
20+
for vector in [v["name"][4:] for v in self._core["vector"] if "EXTI" in v["name"]]:
21+
vrange = sorted(int(d) for d in vector.split("_") if d.isdigit())
22+
if len(vrange) == 2:
23+
vrange = list(range(vrange[0], vrange[1]+1))
24+
for num in vrange:
25+
if num in extimap:
26+
raise ValueError("Pin '{}' already in EXTI map!".format(str(num)))
27+
extimap[num] = vector
28+
return extimap
29+
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2020, Niklas Hauser
5+
# All rights reserved.
6+
7+
from collections import defaultdict
8+
from ..cache import *
9+
from ..access import copy_keys, copy_deep
10+
11+
class DriverGpio:
12+
def __init__(self, device):
13+
self._device = device
14+
self._gpio = self._device.get_driver("gpio")
15+
self.type = self._gpio["type"]
16+
17+
@cached_property
18+
def ranges(self):
19+
"""
20+
Computes all port ranges on this device in the form of a map:
21+
22+
- "name": port.upper()
23+
- "start": min(pin)
24+
- "width": max(pin) - min(pin)
25+
26+
:return: a list of port ranges
27+
"""
28+
ports = defaultdict(list)
29+
for gpio in self._gpio["gpio"]:
30+
ports[gpio["port"]].append(int(gpio["pin"]))
31+
32+
ports = [{"name": k,
33+
"start": min(v),
34+
"width": max(v) - min(v) + 1} for k,v in ports.items()]
35+
ports.sort(key=lambda p: p["name"])
36+
return ports
37+
38+
@cached_property
39+
def ports(self):
40+
"""
41+
Computes all ports on this device.
42+
43+
:return: a sorted unique list of ports in uppercase letters.
44+
"""
45+
ports = set(p["port"] for p in self._gpio["gpio"])
46+
return list(sorted(ports))
47+
48+
@cached_property
49+
def pins(self):
50+
"""
51+
Computes all pins on this device.
52+
53+
:return: a sorted unique list of (port.upper(), int(pin)) tuples.
54+
"""
55+
pins = set((p["port"], int(p["pin"])) for p in self._gpio["gpio"])
56+
return list(sorted(pins))
57+
58+
def signals(self, port, pin):
59+
return self._signals.get((port.lower(), pin))
60+
61+
# @cached_function
62+
def signals_by_name(self, port, pin):
63+
signals = self.signals(port, pin)
64+
names = defaultdict(list)
65+
for s in signals:
66+
names[s["name"]].append(s)
67+
return dict(names)
68+
69+
@cached_property
70+
def signals_remap(self):
71+
return copy_deep(self._gpio.get("remap", []))
72+
73+
@cached_property
74+
def signals_group(self):
75+
sgroup = defaultdict(list)
76+
if "f1" in self.type:
77+
# Convert the map from a list of signals to a list of pins
78+
for remap in self._gpio["remap"]:
79+
for group in remap["group"]:
80+
for signal in group["signal"]:
81+
key = (signal["port"], int(signal["pin"]))
82+
83+
for sig in sgroup[key]:
84+
if ((sig["driver"], sig.get("instance", 0)) ==
85+
(remap["driver"], int(remap.get("instance", 0))) and
86+
sig["name"] == signal["name"]):
87+
sig["group"].append(int(group["id"]))
88+
break
89+
else:
90+
sig = copy_keys(remap, "driver", ("instance", int))
91+
sig["name"] = signal["name"]
92+
sig["group"]= [int(group["id"])]
93+
sgroup[key].append(sig)
94+
return dict(sgroup)
95+
96+
97+
@cached_property
98+
def signals_all(self):
99+
asigs = list()
100+
for signals in self._signals.values():
101+
for s in signals:
102+
asigs.append(copy_keys(s, "name", "driver", ("instance", int)))
103+
return asigs
104+
105+
@cached_property
106+
def _signals(self):
107+
"""
108+
:return:
109+
"""
110+
signals_map = {}
111+
for gpio in self._gpio["gpio"]:
112+
key = (gpio["port"], int(gpio["pin"]))
113+
114+
raw_signals = copy_deep(gpio.get("signal", []))
115+
# raw_signals = gpio.get("signal", [])
116+
if key in self.signals_group:
117+
raw_signals.extend(self.signals_group[key])
118+
119+
for s in raw_signals:
120+
s.update(copy_keys(s, ("af", int), ("instance", int)))
121+
s["is_analog"] = any(s.get("driver", "").startswith(p) for p in {"adc", "dac", "comp"})
122+
if s.get("driver", "").startswith("adc") and s["name"].startswith("in"):
123+
s["analog_channel"] = int("".join(filter(str.isdigit, s["name"])))
124+
125+
signals_map[key] = raw_signals
126+
127+
# print(signals_map)
128+
return signals_map
129+
130+

0 commit comments

Comments
 (0)