python-periphery 2.4.3__py2.py3-none-any.whl → 2.5.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- periphery/__init__.py +2 -2
- periphery/__init__.pyi +1 -0
- periphery/gpio.py +44 -7
- periphery/gpio.pyi +4 -2
- periphery/gpio_cdev1.py +35 -27
- periphery/gpio_cdev2.py +95 -43
- periphery/gpio_sysfs.py +18 -2
- periphery/i2c.py +23 -20
- periphery/i2c.pyi +1 -1
- periphery/led.py +78 -1
- periphery/led.pyi +3 -0
- periphery/mmio.py +46 -2
- periphery/mmio.pyi +2 -0
- periphery/pwm.py +2 -2
- periphery/serial.py +2 -4
- periphery/spi.py +119 -17
- periphery/spi.pyi +9 -1
- {python_periphery-2.4.3.dist-info → python_periphery-2.5.0.dist-info}/METADATA +1 -1
- python_periphery-2.5.0.dist-info/RECORD +25 -0
- {python_periphery-2.4.3.dist-info → python_periphery-2.5.0.dist-info}/WHEEL +1 -1
- python_periphery-2.4.3.dist-info/RECORD +0 -25
- {python_periphery-2.4.3.dist-info → python_periphery-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {python_periphery-2.4.3.dist-info → python_periphery-2.5.0.dist-info}/top_level.txt +0 -0
periphery/__init__.py
CHANGED
periphery/__init__.pyi
CHANGED
|
@@ -12,6 +12,7 @@ from periphery.pwm import PWM as PWM, PWMError as PWMError
|
|
|
12
12
|
from periphery.serial import Serial as Serial, SerialError as SerialError
|
|
13
13
|
from periphery.spi import SPI as SPI, SPIError as SPIError
|
|
14
14
|
|
|
15
|
+
__version__: str
|
|
15
16
|
version: tuple[int, int, int]
|
|
16
17
|
|
|
17
18
|
def sleep(seconds: float) -> None: ...
|
periphery/gpio.py
CHANGED
|
@@ -9,14 +9,12 @@ class GPIOError(IOError):
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class EdgeEvent(collections.namedtuple('EdgeEvent', ['edge', 'timestamp'])):
|
|
12
|
-
|
|
13
|
-
"""EdgeEvent containing the event edge and event time reported by Linux.
|
|
12
|
+
"""EdgeEvent containing the event edge and event time reported by Linux.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return super(EdgeEvent, cls).__new__(cls, edge, timestamp)
|
|
14
|
+
Attributes:
|
|
15
|
+
edge (str): event edge, either "rising" or "falling".
|
|
16
|
+
timestamp (int): event time in nanoseconds.
|
|
17
|
+
"""
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
class GPIO(object):
|
|
@@ -295,6 +293,45 @@ class GPIO(object):
|
|
|
295
293
|
:type: str
|
|
296
294
|
"""
|
|
297
295
|
|
|
296
|
+
def _get_event_clock(self):
|
|
297
|
+
raise NotImplementedError()
|
|
298
|
+
|
|
299
|
+
def _set_event_clock(self, event_clock):
|
|
300
|
+
raise NotImplementedError()
|
|
301
|
+
|
|
302
|
+
event_clock = property(_get_event_clock, _set_event_clock)
|
|
303
|
+
"""Get or set the GPIO's event clock. Can be "realtime", "monotonic",
|
|
304
|
+
"hte".
|
|
305
|
+
|
|
306
|
+
This property is not supported by sysfs GPIOs.
|
|
307
|
+
|
|
308
|
+
Raises:
|
|
309
|
+
GPIOError: if an I/O or OS error occurs.
|
|
310
|
+
TypeError: if `event_clock` type is not str.
|
|
311
|
+
ValueError: if `event_clock` value is invalid.
|
|
312
|
+
|
|
313
|
+
:type: str
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
def _get_debounce_us(self):
|
|
317
|
+
raise NotImplementedError()
|
|
318
|
+
|
|
319
|
+
def _set_debounce_us(self, debounce_us):
|
|
320
|
+
raise NotImplementedError()
|
|
321
|
+
|
|
322
|
+
debounce_us = property(_get_debounce_us, _set_debounce_us)
|
|
323
|
+
"""Get or set the GPIO's debounce period in microseconds.
|
|
324
|
+
|
|
325
|
+
This property is not supported by sysfs GPIOs, nor by ABI version 1
|
|
326
|
+
character device GPIOs.
|
|
327
|
+
|
|
328
|
+
Raises:
|
|
329
|
+
GPIOError: if an I/O or OS error occurs.
|
|
330
|
+
TypeError: if `debounce_us` type is not int.
|
|
331
|
+
|
|
332
|
+
:type: int
|
|
333
|
+
"""
|
|
334
|
+
|
|
298
335
|
def _get_bias(self):
|
|
299
336
|
raise NotImplementedError()
|
|
300
337
|
|
periphery/gpio.pyi
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from types import TracebackType
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
KERNEL_VERSION: tuple[int, int]
|
|
5
|
-
|
|
6
4
|
class GPIOError(IOError): ...
|
|
7
5
|
|
|
8
6
|
class EdgeEvent:
|
|
@@ -38,6 +36,8 @@ class GPIO:
|
|
|
38
36
|
def chip_label(self) -> str: ...
|
|
39
37
|
direction: property
|
|
40
38
|
edge: property
|
|
39
|
+
event_clock: property
|
|
40
|
+
debounce_us: property
|
|
41
41
|
bias: property
|
|
42
42
|
drive: property
|
|
43
43
|
inverted: property
|
|
@@ -53,6 +53,8 @@ class CdevGPIO(GPIO):
|
|
|
53
53
|
drive: str = ...,
|
|
54
54
|
inverted: bool = ...,
|
|
55
55
|
label: str | None = ...,
|
|
56
|
+
event_clock: str = ...,
|
|
57
|
+
debounce_us: int = ...,
|
|
56
58
|
) -> None: ...
|
|
57
59
|
def __new__(self, path: str, line: int | str, direction: str, **kwargs: Any) -> CdevGPIO: ... # noqa: Y034
|
|
58
60
|
|
periphery/gpio_cdev1.py
CHANGED
|
@@ -97,7 +97,7 @@ class Cdev1GPIO(GPIO):
|
|
|
97
97
|
|
|
98
98
|
Instantiate a GPIO object and open the character device GPIO with the
|
|
99
99
|
specified line and direction at the specified GPIO chip path (e.g.
|
|
100
|
-
"/dev/gpiochip0").
|
|
100
|
+
"/dev/gpiochip0"). Default properties can be overridden with keyword
|
|
101
101
|
arguments.
|
|
102
102
|
|
|
103
103
|
Args:
|
|
@@ -170,7 +170,7 @@ class Cdev1GPIO(GPIO):
|
|
|
170
170
|
raise ValueError("Invalid drive, can be: \"default\", \"open_drain\", \"open_source\".")
|
|
171
171
|
|
|
172
172
|
if not isinstance(inverted, bool):
|
|
173
|
-
raise TypeError("Invalid
|
|
173
|
+
raise TypeError("Invalid inverted type, should be bool.")
|
|
174
174
|
|
|
175
175
|
if not isinstance(label, (type(None), str)):
|
|
176
176
|
raise TypeError("Invalid label type, should be None or str.")
|
|
@@ -504,6 +504,33 @@ class Cdev1GPIO(GPIO):
|
|
|
504
504
|
|
|
505
505
|
edge = property(_get_edge, _set_edge)
|
|
506
506
|
|
|
507
|
+
def _get_event_clock(self):
|
|
508
|
+
return "realtime"
|
|
509
|
+
|
|
510
|
+
def _set_event_clock(self, event_clock):
|
|
511
|
+
if not isinstance(event_clock, str):
|
|
512
|
+
raise TypeError("Invalid event_clock type, should be string.")
|
|
513
|
+
if event_clock not in ["realtime", "monotonic", "hte"]:
|
|
514
|
+
raise ValueError("Invalid event_clock, can be: \"realtime\", \"monotonic\", \"hte\".")
|
|
515
|
+
|
|
516
|
+
if event_clock != "realtime":
|
|
517
|
+
raise GPIOError(None, "Kernel version does not support configuring event clock")
|
|
518
|
+
|
|
519
|
+
event_clock = property(_get_event_clock, _set_event_clock)
|
|
520
|
+
|
|
521
|
+
def _get_debounce_us(self):
|
|
522
|
+
return 0
|
|
523
|
+
|
|
524
|
+
def _set_debounce_us(self, debounce_us):
|
|
525
|
+
if not isinstance(debounce_us, int):
|
|
526
|
+
raise TypeError("Invalid debounce_us type, should be integer.")
|
|
527
|
+
if debounce_us < 0:
|
|
528
|
+
raise ValueError("Invalid debounce_us value, should be zero or positive.")
|
|
529
|
+
|
|
530
|
+
raise GPIOError(None, "Kernel version does not support configuring debounce")
|
|
531
|
+
|
|
532
|
+
debounce_us = property(_get_debounce_us, _set_debounce_us)
|
|
533
|
+
|
|
507
534
|
def _get_bias(self):
|
|
508
535
|
return self._bias
|
|
509
536
|
|
|
@@ -544,7 +571,7 @@ class Cdev1GPIO(GPIO):
|
|
|
544
571
|
|
|
545
572
|
def _set_inverted(self, inverted):
|
|
546
573
|
if not isinstance(inverted, bool):
|
|
547
|
-
raise TypeError("Invalid
|
|
574
|
+
raise TypeError("Invalid inverted type, should be bool.")
|
|
548
575
|
|
|
549
576
|
if self._inverted == inverted:
|
|
550
577
|
return
|
|
@@ -566,30 +593,11 @@ class Cdev1GPIO(GPIO):
|
|
|
566
593
|
except GPIOError:
|
|
567
594
|
str_label = "<error>"
|
|
568
595
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
try:
|
|
575
|
-
str_edge = self.edge
|
|
576
|
-
except GPIOError:
|
|
577
|
-
str_edge = "<error>"
|
|
578
|
-
|
|
579
|
-
try:
|
|
580
|
-
str_bias = self.bias
|
|
581
|
-
except GPIOError:
|
|
582
|
-
str_bias = "<error>"
|
|
583
|
-
|
|
584
|
-
try:
|
|
585
|
-
str_drive = self.drive
|
|
586
|
-
except GPIOError:
|
|
587
|
-
str_drive = "<error>"
|
|
588
|
-
|
|
589
|
-
try:
|
|
590
|
-
str_inverted = str(self.inverted)
|
|
591
|
-
except GPIOError:
|
|
592
|
-
str_inverted = "<error>"
|
|
596
|
+
str_direction = self._direction
|
|
597
|
+
str_edge = self._edge
|
|
598
|
+
str_bias = self._bias
|
|
599
|
+
str_drive = self._drive
|
|
600
|
+
str_inverted = str(self._inverted)
|
|
593
601
|
|
|
594
602
|
try:
|
|
595
603
|
str_chip_name = self.chip_name
|
periphery/gpio_cdev2.py
CHANGED
|
@@ -109,6 +109,7 @@ class Cdev2GPIO(GPIO):
|
|
|
109
109
|
_GPIO_V2_LINE_SET_CONFIG_IOCTL = 0xc110b40d
|
|
110
110
|
_GPIO_V2_LINE_SET_VALUES_IOCTL = 0xc010b40f
|
|
111
111
|
_GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES = 0x2
|
|
112
|
+
_GPIO_V2_LINE_ATTR_ID_DEBOUNCE = 0x3
|
|
112
113
|
_GPIO_V2_LINE_EVENT_RISING_EDGE = 0x1
|
|
113
114
|
_GPIO_V2_LINE_EVENT_FALLING_EDGE = 0x2
|
|
114
115
|
_GPIO_V2_LINE_FLAG_ACTIVE_LOW = 0x2
|
|
@@ -122,15 +123,18 @@ class Cdev2GPIO(GPIO):
|
|
|
122
123
|
_GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = 0x200
|
|
123
124
|
_GPIO_V2_LINE_FLAG_BIAS_DISABLED = 0x400
|
|
124
125
|
_GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = 0x800
|
|
126
|
+
_GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE = 0x1000
|
|
125
127
|
|
|
126
128
|
SUPPORTED = KERNEL_VERSION >= (5, 10)
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
_SUPPORTS_EVENT_CLOCK_HTE = KERNEL_VERSION >= (5, 19)
|
|
131
|
+
|
|
132
|
+
def __init__(self, path, line, direction, edge="none", bias="default", drive="default", inverted=False, label=None, event_clock="realtime", debounce_us=0):
|
|
129
133
|
"""**Character device GPIO (ABI version 2)**
|
|
130
134
|
|
|
131
135
|
Instantiate a GPIO object and open the character device GPIO with the
|
|
132
136
|
specified line and direction at the specified GPIO chip path (e.g.
|
|
133
|
-
"/dev/gpiochip0").
|
|
137
|
+
"/dev/gpiochip0"). Default properties can be overridden with keyword
|
|
134
138
|
arguments.
|
|
135
139
|
|
|
136
140
|
Args:
|
|
@@ -146,6 +150,9 @@ class Cdev2GPIO(GPIO):
|
|
|
146
150
|
"open_source".
|
|
147
151
|
inverted (bool): GPIO is inverted (active low).
|
|
148
152
|
label (str, None): GPIO line consumer label.
|
|
153
|
+
event_clock (str): GPIO event clock, can be "realtime",
|
|
154
|
+
"monotonic", or "hte".
|
|
155
|
+
debounce_us (int): GPIO debounce period in microseconds.
|
|
149
156
|
|
|
150
157
|
Returns:
|
|
151
158
|
Cdev2GPIO: GPIO object.
|
|
@@ -153,9 +160,10 @@ class Cdev2GPIO(GPIO):
|
|
|
153
160
|
Raises:
|
|
154
161
|
GPIOError: if an I/O or OS error occurs.
|
|
155
162
|
TypeError: if `path`, `line`, `direction`, `edge`, `bias`, `drive`,
|
|
156
|
-
`inverted`,
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
`inverted`, `label`, `event_clock`, or `debounce_us`
|
|
164
|
+
types are invalid.
|
|
165
|
+
ValueError: if `direction`, `edge`, `bias`, `drive`, `event_clock`,
|
|
166
|
+
or `debounce_us` value is invalid.
|
|
159
167
|
LookupError: if the GPIO line was not found by the provided name.
|
|
160
168
|
|
|
161
169
|
"""
|
|
@@ -165,17 +173,19 @@ class Cdev2GPIO(GPIO):
|
|
|
165
173
|
self._chip_fd = None
|
|
166
174
|
self._direction = None
|
|
167
175
|
self._edge = None
|
|
176
|
+
self._event_clock = None
|
|
177
|
+
self._debounce_us = None
|
|
168
178
|
self._bias = None
|
|
169
179
|
self._drive = None
|
|
170
180
|
self._inverted = None
|
|
171
181
|
self._label = None
|
|
172
182
|
|
|
173
|
-
self._open(path, line, direction, edge, bias, drive, inverted, label)
|
|
183
|
+
self._open(path, line, direction, edge, event_clock, debounce_us, bias, drive, inverted, label)
|
|
174
184
|
|
|
175
185
|
def __new__(self, path, line, direction, **kwargs):
|
|
176
186
|
return object.__new__(Cdev2GPIO)
|
|
177
187
|
|
|
178
|
-
def _open(self, path, line, direction, edge, bias, drive, inverted, label):
|
|
188
|
+
def _open(self, path, line, direction, edge, event_clock, debounce_us, bias, drive, inverted, label):
|
|
179
189
|
if not isinstance(path, str):
|
|
180
190
|
raise TypeError("Invalid path type, should be string.")
|
|
181
191
|
|
|
@@ -192,6 +202,16 @@ class Cdev2GPIO(GPIO):
|
|
|
192
202
|
elif edge not in ["none", "rising", "falling", "both"]:
|
|
193
203
|
raise ValueError("Invalid edge, can be: \"none\", \"rising\", \"falling\", \"both\".")
|
|
194
204
|
|
|
205
|
+
if not isinstance(event_clock, str):
|
|
206
|
+
raise TypeError("Invalid event_clock type, should be string.")
|
|
207
|
+
elif event_clock not in ["realtime", "monotonic", "hte"]:
|
|
208
|
+
raise ValueError("Invalid event_clock, can be: \"realtime\", \"monotonic\", \"hte\".")
|
|
209
|
+
|
|
210
|
+
if not isinstance(debounce_us, int):
|
|
211
|
+
raise TypeError("Invalid debounce_us type, should be integer.")
|
|
212
|
+
elif debounce_us < 0:
|
|
213
|
+
raise ValueError("Invalid debounce_us value, should be zero or positive.")
|
|
214
|
+
|
|
195
215
|
if not isinstance(bias, str):
|
|
196
216
|
raise TypeError("Invalid bias type, should be string.")
|
|
197
217
|
elif bias not in ["default", "pull_up", "pull_down", "disable"]:
|
|
@@ -203,7 +223,7 @@ class Cdev2GPIO(GPIO):
|
|
|
203
223
|
raise ValueError("Invalid drive, can be: \"default\", \"open_drain\", \"open_source\".")
|
|
204
224
|
|
|
205
225
|
if not isinstance(inverted, bool):
|
|
206
|
-
raise TypeError("Invalid
|
|
226
|
+
raise TypeError("Invalid inverted type, should be bool.")
|
|
207
227
|
|
|
208
228
|
if not isinstance(label, (type(None), str)):
|
|
209
229
|
raise TypeError("Invalid label type, should be None or str.")
|
|
@@ -221,11 +241,14 @@ class Cdev2GPIO(GPIO):
|
|
|
221
241
|
self._line = line
|
|
222
242
|
self._label = label.encode() if label is not None else b"periphery"
|
|
223
243
|
|
|
224
|
-
self._reopen(direction, edge, bias, drive, inverted)
|
|
244
|
+
self._reopen(direction, edge, event_clock, debounce_us, bias, drive, inverted)
|
|
225
245
|
|
|
226
|
-
def _reopen(self, direction, edge, bias, drive, inverted):
|
|
246
|
+
def _reopen(self, direction, edge, event_clock, debounce_us, bias, drive, inverted):
|
|
227
247
|
flags = 0
|
|
228
248
|
|
|
249
|
+
if event_clock == "hte" and not Cdev2GPIO._SUPPORTS_EVENT_CLOCK_HTE:
|
|
250
|
+
raise GPIOError(None, "Kernel version does not support GPIO event clock HTE")
|
|
251
|
+
|
|
229
252
|
if bias == "pull_up":
|
|
230
253
|
flags |= Cdev2GPIO._GPIO_V2_LINE_FLAG_BIAS_PULL_UP
|
|
231
254
|
elif bias == "pull_down":
|
|
@@ -258,13 +281,19 @@ class Cdev2GPIO(GPIO):
|
|
|
258
281
|
|
|
259
282
|
if direction == "in":
|
|
260
283
|
flags |= Cdev2GPIO._GPIO_V2_LINE_FLAG_EDGE_RISING if edge == "rising" else Cdev2GPIO._GPIO_V2_LINE_FLAG_EDGE_FALLING if edge == "falling" else (Cdev2GPIO._GPIO_V2_LINE_FLAG_EDGE_RISING | Cdev2GPIO._GPIO_V2_LINE_FLAG_EDGE_FALLING) if edge == "both" else 0
|
|
261
|
-
flags |= Cdev2GPIO._GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME if edge != "none" else 0
|
|
284
|
+
flags |= (Cdev2GPIO._GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME if event_clock == "realtime" else Cdev2GPIO._GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE if event_clock == "hte" else 0) if edge != "none" else 0
|
|
262
285
|
|
|
263
286
|
line_request.offsets[0] = self._line
|
|
264
287
|
line_request.consumer = self._label
|
|
265
288
|
line_request.config.flags = flags | Cdev2GPIO._GPIO_V2_LINE_FLAG_INPUT
|
|
266
289
|
line_request.num_lines = 1
|
|
267
290
|
|
|
291
|
+
if debounce_us:
|
|
292
|
+
line_request.config.num_attrs = 1
|
|
293
|
+
line_request.config.attrs[0].attr.id = Cdev2GPIO._GPIO_V2_LINE_ATTR_ID_DEBOUNCE
|
|
294
|
+
line_request.config.attrs[0].attr.data.debounce_period_us = debounce_us
|
|
295
|
+
line_request.config.attrs[0].mask = 0x1
|
|
296
|
+
|
|
268
297
|
try:
|
|
269
298
|
fcntl.ioctl(self._chip_fd, Cdev2GPIO._GPIO_V2_GET_LINE_IOCTL, line_request)
|
|
270
299
|
except (OSError, IOError) as e:
|
|
@@ -291,6 +320,8 @@ class Cdev2GPIO(GPIO):
|
|
|
291
320
|
|
|
292
321
|
self._direction = "in" if direction == "in" else "out"
|
|
293
322
|
self._edge = edge
|
|
323
|
+
self._event_clock = event_clock
|
|
324
|
+
self._debounce_us = debounce_us
|
|
294
325
|
self._bias = bias
|
|
295
326
|
self._drive = drive
|
|
296
327
|
self._inverted = inverted
|
|
@@ -503,7 +534,7 @@ class Cdev2GPIO(GPIO):
|
|
|
503
534
|
if self._direction == direction:
|
|
504
535
|
return
|
|
505
536
|
|
|
506
|
-
self._reopen(direction, "none", self._bias, self._drive, self._inverted)
|
|
537
|
+
self._reopen(direction, "none", self._event_clock, self._debounce_us, self._bias, self._drive, self._inverted)
|
|
507
538
|
|
|
508
539
|
direction = property(_get_direction, _set_direction)
|
|
509
540
|
|
|
@@ -522,10 +553,48 @@ class Cdev2GPIO(GPIO):
|
|
|
522
553
|
if self._edge == edge:
|
|
523
554
|
return
|
|
524
555
|
|
|
525
|
-
self._reopen(self._direction, edge, self._bias, self._drive, self._inverted)
|
|
556
|
+
self._reopen(self._direction, edge, self._event_clock, self._debounce_us, self._bias, self._drive, self._inverted)
|
|
526
557
|
|
|
527
558
|
edge = property(_get_edge, _set_edge)
|
|
528
559
|
|
|
560
|
+
def _get_event_clock(self):
|
|
561
|
+
return self._event_clock
|
|
562
|
+
|
|
563
|
+
def _set_event_clock(self, event_clock):
|
|
564
|
+
if not isinstance(event_clock, str):
|
|
565
|
+
raise TypeError("Invalid event_clock type, should be string.")
|
|
566
|
+
if event_clock not in ["realtime", "monotonic", "hte"]:
|
|
567
|
+
raise ValueError("Invalid event_clock, can be: \"realtime\", \"monotonic\", \"hte\".")
|
|
568
|
+
|
|
569
|
+
if self._direction != "in":
|
|
570
|
+
raise GPIOError(None, "Invalid operation: cannot set event clock on output GPIO")
|
|
571
|
+
|
|
572
|
+
if self._event_clock == event_clock:
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
self._reopen(self._direction, self._edge, event_clock, self._debounce_us, self._bias, self._drive, self._inverted)
|
|
576
|
+
|
|
577
|
+
event_clock = property(_get_event_clock, _set_event_clock)
|
|
578
|
+
|
|
579
|
+
def _get_debounce_us(self):
|
|
580
|
+
return self._debounce_us
|
|
581
|
+
|
|
582
|
+
def _set_debounce_us(self, debounce_us):
|
|
583
|
+
if not isinstance(debounce_us, int):
|
|
584
|
+
raise TypeError("Invalid debounce_us type, should be integer.")
|
|
585
|
+
if debounce_us < 0:
|
|
586
|
+
raise ValueError("Invalid debounce_us value, should be zero or positive.")
|
|
587
|
+
|
|
588
|
+
if self._direction != "in":
|
|
589
|
+
raise GPIOError(None, "Invalid operation: cannot set debounce on output GPIO")
|
|
590
|
+
|
|
591
|
+
if self._debounce_us == debounce_us:
|
|
592
|
+
return
|
|
593
|
+
|
|
594
|
+
self._reopen(self._direction, self._edge, self._event_clock, debounce_us, self._bias, self._drive, self._inverted)
|
|
595
|
+
|
|
596
|
+
debounce_us = property(_get_debounce_us, _set_debounce_us)
|
|
597
|
+
|
|
529
598
|
def _get_bias(self):
|
|
530
599
|
return self._bias
|
|
531
600
|
|
|
@@ -538,7 +607,7 @@ class Cdev2GPIO(GPIO):
|
|
|
538
607
|
if self._bias == bias:
|
|
539
608
|
return
|
|
540
609
|
|
|
541
|
-
self._reopen(self._direction, self._edge, bias, self._drive, self._inverted)
|
|
610
|
+
self._reopen(self._direction, self._edge, self._event_clock, self._debounce_us, bias, self._drive, self._inverted)
|
|
542
611
|
|
|
543
612
|
bias = property(_get_bias, _set_bias)
|
|
544
613
|
|
|
@@ -557,7 +626,7 @@ class Cdev2GPIO(GPIO):
|
|
|
557
626
|
if self._drive == drive:
|
|
558
627
|
return
|
|
559
628
|
|
|
560
|
-
self._reopen(self._direction, self._edge, self._bias, drive, self._inverted)
|
|
629
|
+
self._reopen(self._direction, self._edge, self._event_clock, self._debounce_us, self._bias, drive, self._inverted)
|
|
561
630
|
|
|
562
631
|
drive = property(_get_drive, _set_drive)
|
|
563
632
|
|
|
@@ -566,12 +635,12 @@ class Cdev2GPIO(GPIO):
|
|
|
566
635
|
|
|
567
636
|
def _set_inverted(self, inverted):
|
|
568
637
|
if not isinstance(inverted, bool):
|
|
569
|
-
raise TypeError("Invalid
|
|
638
|
+
raise TypeError("Invalid inverted type, should be bool.")
|
|
570
639
|
|
|
571
640
|
if self._inverted == inverted:
|
|
572
641
|
return
|
|
573
642
|
|
|
574
|
-
self._reopen(self._direction, self._edge, self._bias, self._drive, inverted)
|
|
643
|
+
self._reopen(self._direction, self._edge, self._event_clock, self._debounce_us, self._bias, self._drive, inverted)
|
|
575
644
|
|
|
576
645
|
inverted = property(_get_inverted, _set_inverted)
|
|
577
646
|
|
|
@@ -588,30 +657,13 @@ class Cdev2GPIO(GPIO):
|
|
|
588
657
|
except GPIOError:
|
|
589
658
|
str_label = "<error>"
|
|
590
659
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
except GPIOError:
|
|
599
|
-
str_edge = "<error>"
|
|
600
|
-
|
|
601
|
-
try:
|
|
602
|
-
str_bias = self.bias
|
|
603
|
-
except GPIOError:
|
|
604
|
-
str_bias = "<error>"
|
|
605
|
-
|
|
606
|
-
try:
|
|
607
|
-
str_drive = self.drive
|
|
608
|
-
except GPIOError:
|
|
609
|
-
str_drive = "<error>"
|
|
610
|
-
|
|
611
|
-
try:
|
|
612
|
-
str_inverted = str(self.inverted)
|
|
613
|
-
except GPIOError:
|
|
614
|
-
str_inverted = "<error>"
|
|
660
|
+
str_direction = self._direction
|
|
661
|
+
str_edge = self._edge
|
|
662
|
+
str_event_clock = self._event_clock
|
|
663
|
+
str_debounce_us = str(self._debounce_us)
|
|
664
|
+
str_bias = self._bias
|
|
665
|
+
str_drive = self._drive
|
|
666
|
+
str_inverted = str(self._inverted)
|
|
615
667
|
|
|
616
668
|
try:
|
|
617
669
|
str_chip_name = self.chip_name
|
|
@@ -623,5 +675,5 @@ class Cdev2GPIO(GPIO):
|
|
|
623
675
|
except GPIOError:
|
|
624
676
|
str_chip_label = "<error>"
|
|
625
677
|
|
|
626
|
-
return "GPIO {:d} (name=\"{:s}\", label=\"{:s}\", device={:s}, line_fd={:d}, chip_fd={:d}, direction={:s}, edge={:s}, bias={:s}, drive={:s}, inverted={:s}, chip_name=\"{:s}\", chip_label=\"{:s}\", type=cdev)" \
|
|
627
|
-
.format(self._line, str_name, str_label, self._devpath, self._line_fd, self._chip_fd, str_direction, str_edge, str_bias, str_drive, str_inverted, str_chip_name, str_chip_label)
|
|
678
|
+
return "GPIO {:d} (name=\"{:s}\", label=\"{:s}\", device={:s}, line_fd={:d}, chip_fd={:d}, direction={:s}, edge={:s}, event_clock={:s}, debounce_us={:s}, bias={:s}, drive={:s}, inverted={:s}, chip_name=\"{:s}\", chip_label=\"{:s}\", type=cdev)" \
|
|
679
|
+
.format(self._line, str_name, str_label, self._devpath, self._line_fd, self._chip_fd, str_direction, str_edge, str_event_clock, str_debounce_us, str_bias, str_drive, str_inverted, str_chip_name, str_chip_label)
|
periphery/gpio_sysfs.py
CHANGED
|
@@ -83,7 +83,7 @@ class SysfsGPIO(GPIO):
|
|
|
83
83
|
with open(os.path.join(gpio_path, "direction"), 'w'):
|
|
84
84
|
break
|
|
85
85
|
except IOError as e:
|
|
86
|
-
if e.errno != errno.EACCES or
|
|
86
|
+
if e.errno != errno.EACCES or i == SysfsGPIO._GPIO_STAT_RETRIES - 1:
|
|
87
87
|
raise GPIOError(e.errno, "Opening GPIO direction: " + e.strerror)
|
|
88
88
|
|
|
89
89
|
time.sleep(SysfsGPIO._GPIO_STAT_DELAY)
|
|
@@ -296,6 +296,22 @@ class SysfsGPIO(GPIO):
|
|
|
296
296
|
|
|
297
297
|
edge = property(_get_edge, _set_edge)
|
|
298
298
|
|
|
299
|
+
def _get_event_clock(self):
|
|
300
|
+
raise NotImplementedError("Sysfs GPIO does not support event clock property.")
|
|
301
|
+
|
|
302
|
+
def _set_event_clock(self, event_clock):
|
|
303
|
+
raise NotImplementedError("Sysfs GPIO does not support event clock property.")
|
|
304
|
+
|
|
305
|
+
event_clock = property(_get_event_clock, _set_event_clock)
|
|
306
|
+
|
|
307
|
+
def _get_debounce_us(self):
|
|
308
|
+
raise NotImplementedError("Sysfs GPIO does not support debounce property.")
|
|
309
|
+
|
|
310
|
+
def _set_debounce_us(self, debounce_us):
|
|
311
|
+
raise NotImplementedError("Sysfs GPIO does not support debounce property.")
|
|
312
|
+
|
|
313
|
+
debounce_us = property(_get_debounce_us, _set_debounce_us)
|
|
314
|
+
|
|
299
315
|
def _get_bias(self):
|
|
300
316
|
raise NotImplementedError("Sysfs GPIO does not support line bias property.")
|
|
301
317
|
|
|
@@ -329,7 +345,7 @@ class SysfsGPIO(GPIO):
|
|
|
329
345
|
|
|
330
346
|
def _set_inverted(self, inverted):
|
|
331
347
|
if not isinstance(inverted, bool):
|
|
332
|
-
raise TypeError("Invalid
|
|
348
|
+
raise TypeError("Invalid inverted type, should be bool.")
|
|
333
349
|
|
|
334
350
|
# Write active_low
|
|
335
351
|
try:
|
periphery/i2c.py
CHANGED
|
@@ -90,40 +90,44 @@ class I2C(object):
|
|
|
90
90
|
|
|
91
91
|
# Methods
|
|
92
92
|
|
|
93
|
-
def transfer(self, address, messages):
|
|
93
|
+
def transfer(self, address, messages, inplace=False):
|
|
94
94
|
"""Transfer `messages` to the specified I2C `address`. Modifies the
|
|
95
95
|
`messages` array with the results of any read transactions.
|
|
96
96
|
|
|
97
97
|
Args:
|
|
98
98
|
address (int): I2C address.
|
|
99
|
-
messages (list): list of I2C.Message messages.
|
|
99
|
+
messages (list, tuple): list of I2C.Message messages.
|
|
100
|
+
inplace (bool): read into `messages` in-place. data must be of type
|
|
101
|
+
`bytearray`.
|
|
100
102
|
|
|
101
103
|
Raises:
|
|
102
104
|
I2CError: if an I/O or OS error occurs.
|
|
103
|
-
TypeError: if `messages`
|
|
104
|
-
ValueError: if `messages` length is zero
|
|
105
|
+
TypeError: if `messages` or message types are invalid.
|
|
106
|
+
ValueError: if `messages` length is zero.
|
|
105
107
|
|
|
106
108
|
"""
|
|
107
|
-
if not isinstance(messages, list):
|
|
108
|
-
raise TypeError("Invalid messages type, should be list of I2C.Message.")
|
|
109
|
+
if not isinstance(messages, (list, tuple)):
|
|
110
|
+
raise TypeError("Invalid messages type, should be list or tuple of I2C.Message.")
|
|
109
111
|
elif len(messages) == 0:
|
|
110
|
-
raise ValueError("Invalid messages
|
|
112
|
+
raise ValueError("Invalid messages, should contain at least one message.")
|
|
113
|
+
for message in messages:
|
|
114
|
+
if not isinstance(message, I2C.Message):
|
|
115
|
+
raise TypeError("Invalid message type, should be I2C.Message.")
|
|
111
116
|
|
|
112
117
|
# Convert I2C.Message messages to _CI2CMessage messages
|
|
113
118
|
cmessages = (_CI2CMessage * len(messages))()
|
|
119
|
+
bufs = []
|
|
120
|
+
|
|
114
121
|
for i in range(len(messages)):
|
|
115
|
-
# Convert
|
|
116
|
-
if isinstance(messages[i].data,
|
|
117
|
-
data = messages[i].data
|
|
118
|
-
elif isinstance(messages[i].data, bytearray):
|
|
119
|
-
data = bytes(messages[i].data)
|
|
120
|
-
elif isinstance(messages[i].data, list):
|
|
121
|
-
data = bytes(bytearray(messages[i].data))
|
|
122
|
+
# Convert data to bytearray (or re-use it if inplace)
|
|
123
|
+
buf = messages[i].data if inplace and isinstance(messages[i].data, bytearray) else bytearray(messages[i].data)
|
|
122
124
|
|
|
123
125
|
cmessages[i].addr = address
|
|
124
126
|
cmessages[i].flags = messages[i].flags | (I2C._I2C_M_RD if messages[i].read else 0)
|
|
125
|
-
cmessages[i].len = len(
|
|
126
|
-
cmessages[i].buf = ctypes.cast(ctypes.
|
|
127
|
+
cmessages[i].len = len(buf)
|
|
128
|
+
cmessages[i].buf = ctypes.cast((ctypes.c_ubyte * len(buf)).from_buffer(buf), ctypes.POINTER(ctypes.c_ubyte))
|
|
129
|
+
|
|
130
|
+
bufs.append(buf)
|
|
127
131
|
|
|
128
132
|
# Prepare transfer structure
|
|
129
133
|
i2c_xfer = _CI2CIocTransfer()
|
|
@@ -139,14 +143,13 @@ class I2C(object):
|
|
|
139
143
|
# Update any read I2C.Message messages
|
|
140
144
|
for i in range(len(messages)):
|
|
141
145
|
if messages[i].read:
|
|
142
|
-
data = [cmessages[i].buf[j] for j in range(cmessages[i].len)]
|
|
143
146
|
# Convert read data to type used in I2C.Message messages
|
|
144
147
|
if isinstance(messages[i].data, list):
|
|
145
|
-
messages[i].data =
|
|
148
|
+
messages[i].data = list(bufs[i])
|
|
146
149
|
elif isinstance(messages[i].data, bytearray):
|
|
147
|
-
messages[i].data =
|
|
150
|
+
messages[i].data = bufs[i]
|
|
148
151
|
elif isinstance(messages[i].data, bytes):
|
|
149
|
-
messages[i].data = bytes(
|
|
152
|
+
messages[i].data = bytes(bufs[i])
|
|
150
153
|
|
|
151
154
|
def close(self):
|
|
152
155
|
"""Close the i2c-dev I2C device.
|
periphery/i2c.pyi
CHANGED
|
@@ -13,7 +13,7 @@ class I2C:
|
|
|
13
13
|
def __del__(self) -> None: ...
|
|
14
14
|
def __enter__(self) -> I2C: ... # noqa: Y034
|
|
15
15
|
def __exit__(self, t: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None) -> None: ...
|
|
16
|
-
def transfer(self, address: int, messages: list[Message]) -> None: ...
|
|
16
|
+
def transfer(self, address: int, messages: list[Message], inplace: bool = ...) -> None: ...
|
|
17
17
|
def close(self) -> None: ...
|
|
18
18
|
@property
|
|
19
19
|
def fd(self) -> int: ...
|
periphery/led.py
CHANGED
|
@@ -154,6 +154,34 @@ class LED(object):
|
|
|
154
154
|
|
|
155
155
|
self._fd = None
|
|
156
156
|
|
|
157
|
+
def _read_trigger(self):
|
|
158
|
+
"""Read and parse the LED "trigger" file.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
(list, str or None): a list of the available triggers, and the
|
|
162
|
+
active trigger or None if no active trigger was found.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
LEDError: if an I/O or OS error occurs.
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
with open(os.path.join(self._path, "trigger"), "r") as f_trigger:
|
|
170
|
+
content = f_trigger.read()
|
|
171
|
+
except IOError as e:
|
|
172
|
+
raise LEDError(e.errno, "Reading LED trigger: " + e.strerror)
|
|
173
|
+
|
|
174
|
+
triggers = []
|
|
175
|
+
active_trigger = None
|
|
176
|
+
for token in content.split():
|
|
177
|
+
if token.startswith("[") and token.endswith("]"):
|
|
178
|
+
active_trigger = token[1:-1]
|
|
179
|
+
triggers.append(active_trigger)
|
|
180
|
+
else:
|
|
181
|
+
triggers.append(token)
|
|
182
|
+
|
|
183
|
+
return triggers, active_trigger
|
|
184
|
+
|
|
157
185
|
# Immutable properties
|
|
158
186
|
|
|
159
187
|
@property
|
|
@@ -189,6 +217,21 @@ class LED(object):
|
|
|
189
217
|
"""
|
|
190
218
|
return self._max_brightness
|
|
191
219
|
|
|
220
|
+
@property
|
|
221
|
+
def triggers(self):
|
|
222
|
+
"""Get the LED's available triggers.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
list: list of available triggers.
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
LEDError: if an I/O or OS error occurs.
|
|
229
|
+
|
|
230
|
+
:type: list
|
|
231
|
+
"""
|
|
232
|
+
triggers, _ = self._read_trigger()
|
|
233
|
+
return triggers
|
|
234
|
+
|
|
192
235
|
# Mutable properties
|
|
193
236
|
|
|
194
237
|
def _get_brightness(self):
|
|
@@ -212,7 +255,41 @@ class LED(object):
|
|
|
212
255
|
:type: int
|
|
213
256
|
"""
|
|
214
257
|
|
|
258
|
+
def _get_trigger(self):
|
|
259
|
+
_, active_trigger = self._read_trigger()
|
|
260
|
+
|
|
261
|
+
if active_trigger is None:
|
|
262
|
+
raise LEDError(None, "Active LED trigger not found.")
|
|
263
|
+
|
|
264
|
+
return active_trigger
|
|
265
|
+
|
|
266
|
+
def _set_trigger(self, trigger):
|
|
267
|
+
if not isinstance(trigger, str):
|
|
268
|
+
raise TypeError("Invalid trigger type, should be string.")
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
with open(os.path.join(self._path, "trigger"), "w") as f_trigger:
|
|
272
|
+
f_trigger.write(trigger + "\n")
|
|
273
|
+
except IOError as e:
|
|
274
|
+
raise LEDError(e.errno, "Setting LED trigger: " + e.strerror)
|
|
275
|
+
|
|
276
|
+
trigger = property(_get_trigger, _set_trigger)
|
|
277
|
+
"""Get or set the LED's active trigger.
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
LEDError: if an I/O or OS error occurs, or if no active trigger is
|
|
281
|
+
found.
|
|
282
|
+
TypeError: if `trigger` type is not str.
|
|
283
|
+
|
|
284
|
+
:type: str
|
|
285
|
+
"""
|
|
286
|
+
|
|
215
287
|
# String representation
|
|
216
288
|
|
|
217
289
|
def __str__(self):
|
|
218
|
-
|
|
290
|
+
try:
|
|
291
|
+
str_trigger = self.trigger
|
|
292
|
+
except LEDError:
|
|
293
|
+
str_trigger = "<error>"
|
|
294
|
+
|
|
295
|
+
return "LED {:s} (device={:s}, fd={:d}, max_brightness={:d}, trigger={:s})".format(self._name, self._path, self._fd, self._max_brightness, str_trigger)
|
periphery/led.pyi
CHANGED
periphery/mmio.py
CHANGED
|
@@ -84,6 +84,28 @@ class MMIO(object):
|
|
|
84
84
|
if (offset + length) > self._aligned_size:
|
|
85
85
|
raise ValueError("Offset out of bounds.")
|
|
86
86
|
|
|
87
|
+
def read64(self, offset):
|
|
88
|
+
"""Read 64-bits from the specified `offset` in bytes, relative to the
|
|
89
|
+
base physical address of the MMIO region.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
offset (int, long): offset from base physical address, in bytes.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
int: 64-bit value read.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
TypeError: if `offset` type is invalid.
|
|
99
|
+
ValueError: if `offset` is out of bounds.
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
if not isinstance(offset, (int, long)):
|
|
103
|
+
raise TypeError("Invalid offset type, should be integer.")
|
|
104
|
+
|
|
105
|
+
offset = self._adjust_offset(offset)
|
|
106
|
+
self._validate_offset(offset, 8)
|
|
107
|
+
return ctypes.c_uint64.from_buffer(self.mapping, offset).value
|
|
108
|
+
|
|
87
109
|
def read32(self, offset):
|
|
88
110
|
"""Read 32-bits from the specified `offset` in bytes, relative to the
|
|
89
111
|
base physical address of the MMIO region.
|
|
@@ -173,6 +195,30 @@ class MMIO(object):
|
|
|
173
195
|
self._validate_offset(offset, length)
|
|
174
196
|
return bytes(self.mapping[offset:offset + length])
|
|
175
197
|
|
|
198
|
+
def write64(self, offset, value):
|
|
199
|
+
"""Write 64-bits to the specified `offset` in bytes, relative to the
|
|
200
|
+
base physical address of the MMIO region.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
offset (int, long): offset from base physical address, in bytes.
|
|
204
|
+
value (int, long): 64-bit value to write.
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
TypeError: if `offset` or `value` type are invalid.
|
|
208
|
+
ValueError: if `offset` or `value` are out of bounds.
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
if not isinstance(offset, (int, long)):
|
|
212
|
+
raise TypeError("Invalid offset type, should be integer.")
|
|
213
|
+
if not isinstance(value, (int, long)):
|
|
214
|
+
raise TypeError("Invalid value type, should be integer.")
|
|
215
|
+
if value < 0 or value > 0xffffffffffffffff:
|
|
216
|
+
raise ValueError("Value out of bounds.")
|
|
217
|
+
|
|
218
|
+
offset = self._adjust_offset(offset)
|
|
219
|
+
self._validate_offset(offset, 8)
|
|
220
|
+
ctypes.c_uint64.from_buffer(self.mapping, offset).value = value
|
|
221
|
+
|
|
176
222
|
def write32(self, offset, value):
|
|
177
223
|
"""Write 32-bits to the specified `offset` in bytes, relative to the
|
|
178
224
|
base physical address of the MMIO region.
|
|
@@ -278,8 +324,6 @@ class MMIO(object):
|
|
|
278
324
|
self.mapping.close()
|
|
279
325
|
self.mapping = None
|
|
280
326
|
|
|
281
|
-
self._fd = None
|
|
282
|
-
|
|
283
327
|
# Immutable properties
|
|
284
328
|
|
|
285
329
|
@property
|
periphery/mmio.pyi
CHANGED
|
@@ -8,10 +8,12 @@ class MMIO:
|
|
|
8
8
|
def __del__(self) -> None: ...
|
|
9
9
|
def __enter__(self) -> MMIO: ... # noqa: Y034
|
|
10
10
|
def __exit__(self, t: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None) -> None: ...
|
|
11
|
+
def read64(self, offset: int) -> int: ...
|
|
11
12
|
def read32(self, offset: int) -> int: ...
|
|
12
13
|
def read16(self, offset: int) -> int: ...
|
|
13
14
|
def read8(self, offset: int) -> int: ...
|
|
14
15
|
def read(self, offset: int, length: int) -> bytes: ...
|
|
16
|
+
def write64(self, offset: int, value: int) -> None: ...
|
|
15
17
|
def write32(self, offset: int, value: int) -> None: ...
|
|
16
18
|
def write16(self, offset: int, value: int) -> None: ...
|
|
17
19
|
def write8(self, offset: int, value: int) -> None: ...
|
periphery/pwm.py
CHANGED
|
@@ -11,7 +11,7 @@ class PWMError(IOError):
|
|
|
11
11
|
class PWM(object):
|
|
12
12
|
# Number of retries to check for successful PWM export on open
|
|
13
13
|
_PWM_STAT_RETRIES = 10
|
|
14
|
-
# Delay between check for
|
|
14
|
+
# Delay between check for successful PWM export on open (100ms)
|
|
15
15
|
_PWM_STAT_DELAY = 0.1
|
|
16
16
|
|
|
17
17
|
def __init__(self, chip, channel):
|
|
@@ -86,7 +86,7 @@ class PWM(object):
|
|
|
86
86
|
with open(os.path.join(channel_path, "period"), 'w'):
|
|
87
87
|
break
|
|
88
88
|
except IOError as e:
|
|
89
|
-
if e.errno != errno.EACCES or
|
|
89
|
+
if e.errno != errno.EACCES or i == PWM._PWM_STAT_RETRIES - 1:
|
|
90
90
|
raise PWMError(e.errno, "Opening PWM period: " + e.strerror)
|
|
91
91
|
|
|
92
92
|
time.sleep(PWM._PWM_STAT_DELAY)
|
periphery/serial.py
CHANGED
|
@@ -168,8 +168,6 @@ class Serial(object):
|
|
|
168
168
|
except termios.error as e:
|
|
169
169
|
raise SerialError(e.errno, "Setting serial port attributes: " + e.strerror)
|
|
170
170
|
|
|
171
|
-
self._use_termios_timeout = False
|
|
172
|
-
|
|
173
171
|
# Methods
|
|
174
172
|
|
|
175
173
|
def read(self, length, timeout=None):
|
|
@@ -268,7 +266,7 @@ class Serial(object):
|
|
|
268
266
|
"""
|
|
269
267
|
p = select.poll()
|
|
270
268
|
p.register(self._fd, select.POLLIN | select.POLLPRI)
|
|
271
|
-
events = p.poll(int(timeout * 1000))
|
|
269
|
+
events = p.poll(int(timeout * 1000) if timeout is not None else None)
|
|
272
270
|
|
|
273
271
|
if len(events) > 0:
|
|
274
272
|
return True
|
|
@@ -317,7 +315,7 @@ class Serial(object):
|
|
|
317
315
|
SerialError: if an I/O or OS error occurs.
|
|
318
316
|
|
|
319
317
|
"""
|
|
320
|
-
# Get
|
|
318
|
+
# Get output waiting
|
|
321
319
|
buf = array.array('I', [0])
|
|
322
320
|
try:
|
|
323
321
|
fcntl.ioctl(self._fd, termios.TIOCOUTQ, buf, True)
|
periphery/spi.py
CHANGED
|
@@ -27,7 +27,8 @@ class _CSpiIocTransfer(ctypes.Structure):
|
|
|
27
27
|
('cs_change', ctypes.c_ubyte),
|
|
28
28
|
('tx_nbits', ctypes.c_ubyte),
|
|
29
29
|
('rx_nbits', ctypes.c_ubyte),
|
|
30
|
-
('
|
|
30
|
+
('word_delay_usecs', ctypes.c_ubyte),
|
|
31
|
+
('pad', ctypes.c_ubyte),
|
|
31
32
|
]
|
|
32
33
|
|
|
33
34
|
|
|
@@ -45,8 +46,10 @@ class SPI(object):
|
|
|
45
46
|
_SPI_IOC_WR_BITS_PER_WORD = 0x40016b03
|
|
46
47
|
_SPI_IOC_RD_BITS_PER_WORD = 0x80016b03
|
|
47
48
|
_SPI_IOC_MESSAGE_1 = 0x40206b00
|
|
49
|
+
_SPI_IOC_MAGIC = 0x6b
|
|
48
50
|
|
|
49
51
|
_SUPPORTS_MODE32 = KERNEL_VERSION >= (3, 15)
|
|
52
|
+
_SUPPORTS_WORD_DELAY = KERNEL_VERSION >= (5, 2)
|
|
50
53
|
|
|
51
54
|
def __init__(self, devpath, mode, max_speed, bit_order="msb", bits_per_word=8, extra_flags=0):
|
|
52
55
|
"""Instantiate a SPI object and open the spidev device at the specified
|
|
@@ -149,11 +152,14 @@ class SPI(object):
|
|
|
149
152
|
|
|
150
153
|
# Methods
|
|
151
154
|
|
|
152
|
-
def transfer(self, data):
|
|
155
|
+
def transfer(self, data, inplace=False):
|
|
153
156
|
"""Shift out `data` and return shifted in data.
|
|
154
157
|
|
|
155
158
|
Args:
|
|
156
|
-
data (bytes, bytearray, list): a byte array or list of 8-bit
|
|
159
|
+
data (bytes, bytearray, list): a byte array or list of 8-bit
|
|
160
|
+
integers to shift out.
|
|
161
|
+
inplace (bool): read into `data` in-place. data must be of type
|
|
162
|
+
`bytearray`.
|
|
157
163
|
|
|
158
164
|
Returns:
|
|
159
165
|
bytes, bytearray, list: data shifted in.
|
|
@@ -161,25 +167,19 @@ class SPI(object):
|
|
|
161
167
|
Raises:
|
|
162
168
|
SPIError: if an I/O or OS error occurs.
|
|
163
169
|
TypeError: if `data` type is invalid.
|
|
164
|
-
ValueError: if data is not valid bytes.
|
|
165
170
|
|
|
166
171
|
"""
|
|
167
172
|
if not isinstance(data, (bytes, bytearray, list)):
|
|
168
173
|
raise TypeError("Invalid data type, should be bytes, bytearray, or list.")
|
|
169
174
|
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
buf = array.array('B', data)
|
|
173
|
-
except OverflowError:
|
|
174
|
-
raise ValueError("Invalid data bytes.")
|
|
175
|
-
|
|
176
|
-
buf_addr, buf_len = buf.buffer_info()
|
|
175
|
+
# Convert data to bytearray (or re-use it if inplace)
|
|
176
|
+
buf = data if inplace and isinstance(data, bytearray) else bytearray(data)
|
|
177
177
|
|
|
178
178
|
# Prepare transfer structure
|
|
179
179
|
spi_xfer = _CSpiIocTransfer()
|
|
180
|
-
spi_xfer.tx_buf =
|
|
181
|
-
spi_xfer.rx_buf =
|
|
182
|
-
spi_xfer.len =
|
|
180
|
+
spi_xfer.tx_buf = ctypes.addressof((ctypes.c_ubyte * len(data)).from_buffer(buf))
|
|
181
|
+
spi_xfer.rx_buf = spi_xfer.tx_buf
|
|
182
|
+
spi_xfer.len = len(data)
|
|
183
183
|
|
|
184
184
|
# Transfer
|
|
185
185
|
try:
|
|
@@ -189,11 +189,90 @@ class SPI(object):
|
|
|
189
189
|
|
|
190
190
|
# Return shifted out data with the same type as shifted in data
|
|
191
191
|
if isinstance(data, bytes):
|
|
192
|
-
return bytes(
|
|
192
|
+
return bytes(buf)
|
|
193
193
|
elif isinstance(data, bytearray):
|
|
194
|
-
return
|
|
194
|
+
return buf
|
|
195
195
|
elif isinstance(data, list):
|
|
196
|
-
return buf
|
|
196
|
+
return list(buf)
|
|
197
|
+
|
|
198
|
+
def transfer_advanced(self, messages, inplace=False):
|
|
199
|
+
"""Transfer multiple messages, shifting out `data` of each message,
|
|
200
|
+
while shifting the same length in, modifying the `messages` list with
|
|
201
|
+
the shifted in data of each message. If `deselect` is true, deselect
|
|
202
|
+
the device before reselecting it for the following transfer.
|
|
203
|
+
|
|
204
|
+
`deselect_delay_us` specifies a delay in microseconds before
|
|
205
|
+
deselection when `deselect` is true. `word_delay_us` specifies a delay
|
|
206
|
+
in microseconds between words within a message. Note that
|
|
207
|
+
`deselect_delay_us` and `word_delay_us` may not be supported by all SPI
|
|
208
|
+
controllers and may be silently ignored.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
messages (list, tuple): list of SPI.Message messages to transfer.
|
|
212
|
+
inplace (bool): read into `messages` in-place. data must be of type
|
|
213
|
+
`bytearray`.
|
|
214
|
+
|
|
215
|
+
Raises:
|
|
216
|
+
SPIError: if an I/O or OS error occurs.
|
|
217
|
+
TypeError: if `messages` or message types are invalid.
|
|
218
|
+
ValueError: if `messages` length is zero.
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
if not isinstance(messages, (list, tuple)):
|
|
222
|
+
raise TypeError("Invalid messages type, should be list or tuple of SPI.Message.")
|
|
223
|
+
elif len(messages) == 0:
|
|
224
|
+
raise ValueError("Invalid messages, should contain at least one message.")
|
|
225
|
+
for message in messages:
|
|
226
|
+
if not isinstance(message, SPI.Message):
|
|
227
|
+
raise TypeError("Invalid message type, should be SPI.Message.")
|
|
228
|
+
|
|
229
|
+
# Prepare transfer structures
|
|
230
|
+
spi_xfer = (_CSpiIocTransfer * len(messages))()
|
|
231
|
+
bufs = []
|
|
232
|
+
|
|
233
|
+
for i, message in enumerate(messages):
|
|
234
|
+
# Validate message
|
|
235
|
+
if not isinstance(message.data, (bytes, bytearray, list)):
|
|
236
|
+
raise TypeError("Invalid data type in message {:d}, should be bytes, bytearray, or list.".format(i))
|
|
237
|
+
if not isinstance(message.deselect, bool):
|
|
238
|
+
raise TypeError("Invalid deselect type in message {:d}, should be bool.".format(i))
|
|
239
|
+
if not isinstance(message.deselect_delay_us, int):
|
|
240
|
+
raise TypeError("Invalid deselect_delay_us type in message {:d}, should be integer.".format(i))
|
|
241
|
+
if not isinstance(message.word_delay_us, int):
|
|
242
|
+
raise TypeError("Invalid word_delay_us type in message {:d}, should be integer.".format(i))
|
|
243
|
+
|
|
244
|
+
# Convert data to bytearray (or re-use it if inplace)
|
|
245
|
+
buf = message.data if inplace and isinstance(message.data, bytearray) else bytearray(message.data)
|
|
246
|
+
|
|
247
|
+
spi_xfer[i].tx_buf = ctypes.addressof((ctypes.c_ubyte * len(buf)).from_buffer(buf))
|
|
248
|
+
spi_xfer[i].rx_buf = spi_xfer[i].tx_buf
|
|
249
|
+
spi_xfer[i].len = len(buf)
|
|
250
|
+
spi_xfer[i].delay_usecs = message.deselect_delay_us
|
|
251
|
+
spi_xfer[i].cs_change = int(message.deselect)
|
|
252
|
+
if SPI._SUPPORTS_WORD_DELAY:
|
|
253
|
+
spi_xfer[i].word_delay_usecs = message.word_delay_us
|
|
254
|
+
|
|
255
|
+
bufs.append(buf)
|
|
256
|
+
|
|
257
|
+
# Prepare the SPI_IOC_MESSAGE ioctl number for message count:
|
|
258
|
+
# _IOW(SPI_IOC_MAGIC, 0, struct spi_ioc_transfer[count])
|
|
259
|
+
spi_ioc_message = (1 << 30) | ((ctypes.sizeof(_CSpiIocTransfer) * len(messages)) << 16) | (SPI._SPI_IOC_MAGIC << 8) | 0x0
|
|
260
|
+
|
|
261
|
+
# Transfer
|
|
262
|
+
try:
|
|
263
|
+
fcntl.ioctl(self._fd, spi_ioc_message, spi_xfer)
|
|
264
|
+
except (OSError, IOError) as e:
|
|
265
|
+
raise SPIError(e.errno, "SPI transfer: " + e.strerror)
|
|
266
|
+
|
|
267
|
+
# Update SPI.Message data with the shifted in data
|
|
268
|
+
for i in range(len(messages)):
|
|
269
|
+
# Convert shifted in data to type used in SPI.Message messages
|
|
270
|
+
if isinstance(messages[i].data, list):
|
|
271
|
+
messages[i].data = list(bufs[i])
|
|
272
|
+
elif isinstance(messages[i].data, bytearray):
|
|
273
|
+
messages[i].data = bufs[i]
|
|
274
|
+
elif isinstance(messages[i].data, bytes):
|
|
275
|
+
messages[i].data = bytes(bufs[i])
|
|
197
276
|
|
|
198
277
|
def close(self):
|
|
199
278
|
"""Close the spidev SPI device.
|
|
@@ -453,3 +532,26 @@ class SPI(object):
|
|
|
453
532
|
def __str__(self):
|
|
454
533
|
return "SPI (device={:s}, fd={:d}, mode={:d}, max_speed={:d}, bit_order={:s}, bits_per_word={:d}, extra_flags=0x{:08x})" \
|
|
455
534
|
.format(self.devpath, self.fd, self.mode, self.max_speed, self.bit_order, self.bits_per_word, self.extra_flags)
|
|
535
|
+
|
|
536
|
+
class Message:
|
|
537
|
+
def __init__(self, data, deselect=False, deselect_delay_us=0, word_delay_us=0):
|
|
538
|
+
"""Instantiate an SPI Message object.
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
data (bytes, bytearray, list): a byte array or list of 8-bit
|
|
542
|
+
integers to shift out.
|
|
543
|
+
deselect (bool): deselect the device before reselecting it for
|
|
544
|
+
the following transfer.
|
|
545
|
+
deselect_delay_us (int): delay in microseconds before
|
|
546
|
+
deselection when `deselect` is true.
|
|
547
|
+
word_delay_us (int): delay in microseconds between words within
|
|
548
|
+
the transfer.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
Message: Message object.
|
|
552
|
+
|
|
553
|
+
"""
|
|
554
|
+
self.data = data
|
|
555
|
+
self.deselect = deselect
|
|
556
|
+
self.deselect_delay_us = deselect_delay_us
|
|
557
|
+
self.word_delay_us = word_delay_us
|
periphery/spi.pyi
CHANGED
|
@@ -5,13 +5,21 @@ KERNEL_VERSION: tuple[int, int]
|
|
|
5
5
|
class SPIError(IOError): ...
|
|
6
6
|
|
|
7
7
|
class SPI:
|
|
8
|
+
class Message:
|
|
9
|
+
data: bytes | bytearray | list[int]
|
|
10
|
+
deselect: bool
|
|
11
|
+
deselect_delay_us: int
|
|
12
|
+
word_delay_us: int
|
|
13
|
+
def __init__(self, data: bytes | bytearray | list[int], deselect: bool = ..., deselect_delay_us: int = ..., word_delay_us: int = ...) -> None: ...
|
|
14
|
+
|
|
8
15
|
def __init__(
|
|
9
16
|
self, devpath: str, mode: int, max_speed: float, bit_order: str = ..., bits_per_word: int = ..., extra_flags: int = ...
|
|
10
17
|
) -> None: ...
|
|
11
18
|
def __del__(self) -> None: ...
|
|
12
19
|
def __enter__(self) -> SPI: ... # noqa: Y034
|
|
13
20
|
def __exit__(self, t: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None) -> None: ...
|
|
14
|
-
def transfer(self, data: bytes | bytearray | list[int]) -> bytes | bytearray | list[int]: ...
|
|
21
|
+
def transfer(self, data: bytes | bytearray | list[int], inplace: bool = ...) -> bytes | bytearray | list[int]: ...
|
|
22
|
+
def transfer_advanced(self, messages: list[Message], inplace: bool = ...) -> None: ...
|
|
15
23
|
def close(self) -> None: ...
|
|
16
24
|
@property
|
|
17
25
|
def fd(self) -> int: ...
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
periphery/__init__.py,sha256=BxvDYdmw2XYtIWBnFOTBgoSWVEp8s1HdjD45a3SdoVk,1030
|
|
2
|
+
periphery/__init__.pyi,sha256=i2GDAnpXrUiDJTHGRop9VQqbSbyPc5bxwTXgEa0_T04,715
|
|
3
|
+
periphery/gpio.py,sha256=Gr63tO6AkBxFnozqbm_VueL-vp5LxTtAmLzzFUHEUO8,10903
|
|
4
|
+
periphery/gpio.pyi,sha256=McFZrDm1CVjw34uiK6LNGb94nSTpU5wBaDI84E1Q3lg,2018
|
|
5
|
+
periphery/gpio_cdev1.py,sha256=m14lAszb3VWTaxtJnypPTvDfmtyINGUH1JFlwIKvLOY,21348
|
|
6
|
+
periphery/gpio_cdev2.py,sha256=TnCprCQOr5ZxLl9HY7ati6_zt1OT-NsSbZXNoaLHGbU,24511
|
|
7
|
+
periphery/gpio_sysfs.py,sha256=ur-NclCxF7-uycbrPh1sE8Ny7GJMtkMzhvZam98kwns,12504
|
|
8
|
+
periphery/i2c.py,sha256=bBM0752cBr6Qwkk5edl7dGPTKCD7rNXKYzgYzXQTlcs,6858
|
|
9
|
+
periphery/i2c.pyi,sha256=StHTVYD1G6l37OEoKi-weKVuEykbtF9HjswsakxFanQ,773
|
|
10
|
+
periphery/led.py,sha256=5l5XwG2weKKQpkapmfhA7PEJJiBltQsbXSU6cK2KeL0,8234
|
|
11
|
+
periphery/led.pyi,sha256=EYVIRvuWXiZvThuGl_w3fKIPb4c1bXiViph8fwnsOO8,776
|
|
12
|
+
periphery/mmio.py,sha256=0rup6NJADc9XGhxFXH1sK5DNdhmta5ho945-ckTRW20,12134
|
|
13
|
+
periphery/mmio.pyi,sha256=8jI5e5z43XnZRtlpwUxPywmMTdt-BF4mtrurZ3iJwEg,1138
|
|
14
|
+
periphery/pwm.py,sha256=8opSA9ZJmrW_5rrRiIynAlJbIYTXNd7vSskZPVAv_kQ,10190
|
|
15
|
+
periphery/pwm.pyi,sha256=bHNZzrtRGRv7KSVL_-4zR8bjBt1JXYFlf1mhc0vu5TE,749
|
|
16
|
+
periphery/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
periphery/serial.py,sha256=qmcQo5gKk2SC7jcDtjNGRfa7WFUQJEPi4jn2zZUF46M,25266
|
|
18
|
+
periphery/serial.pyi,sha256=LCU7XhhD-yyCGG5cWM7C-K0r-FOaUydGo4GB1v9KD_Y,1114
|
|
19
|
+
periphery/spi.py,sha256=iMrl70HX1P5IqNYgxNs_RnKJ3Z7URdnsYc2iAYRvRus,20647
|
|
20
|
+
periphery/spi.pyi,sha256=902eU5rHGilmub6D1NtGHWLofZHRQHFEL4ll9utuftk,1218
|
|
21
|
+
python_periphery-2.5.0.dist-info/licenses/LICENSE,sha256=uv-e__4tQsDlgnHRd8NFan5DbsfFxHoC2pG7x23oOHc,1099
|
|
22
|
+
python_periphery-2.5.0.dist-info/METADATA,sha256=vpRvyehCsjPMkPZgyDKyr_glLto9fJLSzkc5aqq6uk4,1629
|
|
23
|
+
python_periphery-2.5.0.dist-info/WHEEL,sha256=Uvlg2aNlEKdilsyFSJt1QX5me0eEbCjQzR3nZeFkvy4,109
|
|
24
|
+
python_periphery-2.5.0.dist-info/top_level.txt,sha256=b4Woy5NSzNa7ZPLaz2cmCExhZP2-5zOgtUZog8ASZy0,10
|
|
25
|
+
python_periphery-2.5.0.dist-info/RECORD,,
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
periphery/__init__.py,sha256=bFlQwbt4Eo3p9VjzuaRpVIhbPyIKhX50EOXmNi_Maak,1030
|
|
2
|
-
periphery/__init__.pyi,sha256=kNV-srVZuc_gxTFhINQaQhFYhwfxePFOoW4zwZysiyA,698
|
|
3
|
-
periphery/gpio.py,sha256=JhDIEcC2BEmpSKXlnAxNkeM3pvzaVClE42yAER_lEuw,9960
|
|
4
|
-
periphery/gpio.pyi,sha256=3xHOcsmXxdcc-sXy3KNb0beuCp-KCW759Duq1jr88J4,1935
|
|
5
|
-
periphery/gpio_cdev1.py,sha256=QdGcPRLQlboz64M1_TL-cjcrQcqdcIMRT1HnHC0Ksxk,20665
|
|
6
|
-
periphery/gpio_cdev2.py,sha256=84nMmPrtJ0StJcAwE8iw45YadV45CpHBrmLBruomzGw,21199
|
|
7
|
-
periphery/gpio_sysfs.py,sha256=da-8uMziBAA_wQOaEWO8DXysilOndOMVrRLmtQUc8zs,11903
|
|
8
|
-
periphery/i2c.py,sha256=tMS7XQ10DAblbqinSHHjYB6xNUPns71_JZvfm1cLZPc,6769
|
|
9
|
-
periphery/i2c.pyi,sha256=dfWclTARt3iCvIwMoV1IwiUS5WyssSrPm_nE-gLyUHg,752
|
|
10
|
-
periphery/led.py,sha256=waQ3nrlzyvTBKqZQpPva4AuyITGoUirJ5ROVUeklxAE,5997
|
|
11
|
-
periphery/led.pyi,sha256=f_lUJNlamKkbbH2Ivj-ZUGylXC4bVVINp2NcQXzYTSY,704
|
|
12
|
-
periphery/mmio.py,sha256=fsdycYJ2GqLTRN_AIWV-GpTCYsU0jBoe_T9MZfiIw8E,10469
|
|
13
|
-
periphery/mmio.pyi,sha256=_Ysa_DI6d6PRoepMD_CY4zb2wHmDo6smGEtnHP4fk4E,1032
|
|
14
|
-
periphery/pwm.py,sha256=5Z-eIHnE3SXozUOWD5bB3R1LVHuLaFWqOw28Qbm4BFg,10220
|
|
15
|
-
periphery/pwm.pyi,sha256=bHNZzrtRGRv7KSVL_-4zR8bjBt1JXYFlf1mhc0vu5TE,749
|
|
16
|
-
periphery/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
periphery/serial.py,sha256=L-tNg7aOTL_-c4tJ2qOXtp1I8LMRgYFmQWLnMmW2VCo,25275
|
|
18
|
-
periphery/serial.pyi,sha256=LCU7XhhD-yyCGG5cWM7C-K0r-FOaUydGo4GB1v9KD_Y,1114
|
|
19
|
-
periphery/spi.py,sha256=LTEpKBvvrhIdfcav-Zr08JKt_75BMYLaPoOYBCPzvtg,15568
|
|
20
|
-
periphery/spi.pyi,sha256=xpDxe72ffXmjT905fMwJDHTMZgTSxOY4Wtpk-JJiBrU,806
|
|
21
|
-
python_periphery-2.4.3.dist-info/licenses/LICENSE,sha256=uv-e__4tQsDlgnHRd8NFan5DbsfFxHoC2pG7x23oOHc,1099
|
|
22
|
-
python_periphery-2.4.3.dist-info/METADATA,sha256=B7WyU7pOHadweyN-LfD02Uq74NCfAU29W-VWQJa9wgU,1629
|
|
23
|
-
python_periphery-2.4.3.dist-info/WHEEL,sha256=4YBfCYNH4wlLpv3pzq1hbEuIlXA4WJabKLFurZ7eTL0,109
|
|
24
|
-
python_periphery-2.4.3.dist-info/top_level.txt,sha256=b4Woy5NSzNa7ZPLaz2cmCExhZP2-5zOgtUZog8ASZy0,10
|
|
25
|
-
python_periphery-2.4.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|