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 CHANGED
@@ -1,9 +1,9 @@
1
1
  import time
2
2
 
3
- __version__ = "2.4.3"
3
+ __version__ = "2.5.0"
4
4
  "Module version string."
5
5
 
6
- version = (2, 4, 3)
6
+ version = (2, 5, 0)
7
7
  "Module version tuple."
8
8
 
9
9
 
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
- def __new__(cls, edge, timestamp):
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
- Args:
16
- edge (str): event edge, either "rising" or "falling".
17
- timestamp (int): event time in nanoseconds.
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"). Defaults properties can be overridden with keyword
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 drive type, should be bool.")
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 drive type, should be bool.")
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
- try:
570
- str_direction = self.direction
571
- except GPIOError:
572
- str_direction = "<error>"
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
- def __init__(self, path, line, direction, edge="none", bias="default", drive="default", inverted=False, label=None):
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"). Defaults properties can be overridden with keyword
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`, or `label` types are invalid.
157
- ValueError: if `direction`, `edge`, `bias`, or `drive` value is
158
- invalid.
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 drive type, should be bool.")
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 drive type, should be bool.")
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
- try:
592
- str_direction = self.direction
593
- except GPIOError:
594
- str_direction = "<error>"
595
-
596
- try:
597
- str_edge = self.edge
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 (e.errno == errno.EACCES and i == SysfsGPIO._GPIO_STAT_RETRIES - 1):
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 drive type, should be bool.")
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` type is not list.
104
- ValueError: if `messages` length is zero, or if message data is not valid bytes.
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 data, should be non-zero length.")
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 I2C.Message data to bytes
116
- if isinstance(messages[i].data, bytes):
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(data)
126
- cmessages[i].buf = ctypes.cast(ctypes.create_string_buffer(data, len(data)), ctypes.POINTER(ctypes.c_ubyte))
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 = data
148
+ messages[i].data = list(bufs[i])
146
149
  elif isinstance(messages[i].data, bytearray):
147
- messages[i].data = bytearray(data)
150
+ messages[i].data = bufs[i]
148
151
  elif isinstance(messages[i].data, bytes):
149
- messages[i].data = bytes(bytearray(data))
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
- return "LED {:s} (device={:s}, fd={:d}, max_brightness={:d})".format(self._name, self._path, self._fd, self._max_brightness)
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
@@ -18,4 +18,7 @@ class LED:
18
18
  def name(self) -> str: ...
19
19
  @property
20
20
  def max_brightness(self) -> int: ...
21
+ @property
22
+ def triggers(self) -> list[str]: ...
23
+ trigger: str
21
24
  brightness: int
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 scucessful PWM export on open (100ms)
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 (e.errno == errno.EACCES and i == PWM._PWM_STAT_RETRIES - 1):
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 input waiting
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
- ('pad', ctypes.c_ushort),
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 integers to shift out.
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
- # Create mutable array
171
- try:
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 = buf_addr
181
- spi_xfer.rx_buf = buf_addr
182
- spi_xfer.len = buf_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(bytearray(buf))
192
+ return bytes(buf)
193
193
  elif isinstance(data, bytearray):
194
- return bytearray(buf)
194
+ return buf
195
195
  elif isinstance(data, list):
196
- return buf.tolist()
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: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-periphery
3
- Version: 2.4.3
3
+ Version: 2.5.0
4
4
  Summary: A pure Python 2/3 library for peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO, Serial) in Linux.
5
5
  Home-page: https://github.com/vsergeev/python-periphery
6
6
  Author: vsergeev
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -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,,