Skip to content

Commit a499cb8

Browse files
nkpro2000srbessman
authored andcommitted
Add busio.UART
1 parent 55048be commit a499cb8

File tree

1 file changed

+216
-4
lines changed

1 file changed

+216
-4
lines changed

pslab/bus/busio.py

Lines changed: 216 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,21 @@
2828
>>> print(sensor.gyro)
2929
"""
3030

31-
from typing import List, Union
31+
import time
32+
from enum import Enum
33+
from itertools import zip_longest
34+
from typing import List, Union, Optional
3235

3336
from pslab.bus.i2c import _I2CPrimitive
3437
from pslab.bus.spi import _SPIPrimitive
38+
from pslab.bus.uart import _UARTPrimitive
3539
from pslab.serial_handler import SerialHandler
3640

37-
__all__ = "I2C"
41+
__all__ = (
42+
"I2C",
43+
"SPI",
44+
"UART",
45+
)
3846
ReadableBuffer = Union[bytes, bytearray, memoryview]
3947
WriteableBuffer = Union[bytearray, memoryview]
4048

@@ -51,7 +59,7 @@ class I2C(_I2CPrimitive):
5159
Frequency of SCL in Hz.
5260
"""
5361

54-
def __init__(self, device: SerialHandler = None, frequency: int = 125e3):
62+
def __init__(self, device: SerialHandler = None, *, frequency: int = 125e3):
5563
# 125 kHz is as low as the PSLab can go.
5664
super().__init__(device)
5765
self._init()
@@ -191,7 +199,7 @@ class SPI(_SPIPrimitive):
191199
created.
192200
"""
193201

194-
def __init__(self, device: SerialHandler = None, frequency: int = 125e3):
202+
def __init__(self, device: SerialHandler = None):
195203
super().__init__(device)
196204
ppre, spre = self._get_prescaler(25e4)
197205
self._set_parameters(ppre, spre, 1, 0, 1)
@@ -373,3 +381,207 @@ def write_readinto(
373381

374382
for i, v in zip(range(in_start, in_end), data):
375383
buffer_in[i] = v
384+
385+
386+
class Parity(Enum):
387+
EVEN = 1
388+
ODD = 2
389+
390+
391+
class UART(_UARTPrimitive):
392+
"""Busio UART Class for CircuitPython Compatibility.
393+
394+
Parameters
395+
----------
396+
device : :class:`SerialHandler`, optional
397+
Serial connection to PSLab device. If not provided, a new one will be
398+
created.
399+
baudrate : int, optional
400+
The transmit and receive speed. Defaults to 9600.
401+
bits : int, optional
402+
The number of bits per byte, 8 or 9. Defaults to 8 bits.
403+
parity : :class:`Parity`, optional
404+
The parity used for error checking. Defaults to None.
405+
Only 8 bits per byte supports parity.
406+
stop : int, optional
407+
The number of stop bits, 1 or 2. Defaults to 1.
408+
timeout : float, optional
409+
The timeout in seconds to wait for the first character and between
410+
subsequent characters when reading. Defaults to 1.
411+
"""
412+
413+
def __init__(
414+
self,
415+
device: SerialHandler = None,
416+
*,
417+
baudrate: int = 9600,
418+
bits: int = 8,
419+
parity: Parity = None,
420+
stop: int = 1,
421+
timeout: float = 1,
422+
):
423+
super().__init__(device)
424+
self._set_uart_baud(baudrate)
425+
426+
if bits == 8:
427+
pd = 0
428+
elif bits == 9:
429+
pd = 3
430+
else:
431+
raise ValueError("Invalid number of bits")
432+
433+
if bits == 9 and parity is not None:
434+
raise ValueError("Invalid parity")
435+
if stop not in (1, 2):
436+
raise ValueError("Invalid number of stop bits")
437+
438+
pd += parity.value
439+
440+
try:
441+
self._set_uart_mode(pd, stop - 1)
442+
except RuntimeError:
443+
pass
444+
445+
self._timeout = timeout
446+
447+
@property
448+
def baudrate(self):
449+
"""Get the current baudrate."""
450+
return self._baudrate
451+
452+
@property
453+
def in_waiting(self):
454+
"""Get the number of bytes in the input buffer, available to be read.
455+
456+
PSLab is limited to check, whether at least one byte in the buffer(1) or not(0).
457+
"""
458+
return self._read_uart_status()
459+
460+
@property
461+
def timeout(self):
462+
"""Get the current timeout, in seconds (float)."""
463+
return self._timeout
464+
465+
def deinit(self) -> None:
466+
"""Just a dummy method."""
467+
pass
468+
469+
def __enter__(self):
470+
"""Just a dummy context manager."""
471+
return self
472+
473+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
474+
"""Call :meth:`deinit` on context exit."""
475+
self.deinit()
476+
477+
def _read_with_timeout(self, nbytes: int = None, *, line=False):
478+
if nbytes == 0:
479+
return None
480+
481+
start_time = time.time()
482+
data = bytearray()
483+
total_read = 0
484+
485+
while (time.time() - start_time) <= self._timeout:
486+
has_char = self._read_uart_status()
487+
if has_char:
488+
char = self._read_byte()
489+
start_time = time.time()
490+
491+
if line and char == 0xA:
492+
break
493+
494+
data.append(char)
495+
total_read += 1
496+
497+
if nbytes and total_read == nbytes:
498+
break
499+
500+
time.sleep(0.01)
501+
502+
return bytes(data) if data else None
503+
504+
def read(self, nbytes: int = None) -> Optional[bytes]:
505+
"""Read characters.
506+
507+
If `nbytes` is specified then read at most that many bytes. Otherwise,
508+
read everything that arrives until the connection times out.
509+
510+
Providing the number of bytes expected is highly recommended because
511+
it will be faster.
512+
513+
Parameters
514+
----------
515+
nbytes : int, optional
516+
Number of bytes to read. Defaults to None.
517+
518+
Returns
519+
-------
520+
bytes or None
521+
Data read.
522+
"""
523+
return self._read_with_timeout(nbytes)
524+
525+
def readinto(self, buf: WriteableBuffer) -> int:
526+
"""Read bytes into the `buf`. Read at most `len(buf)` bytes.
527+
528+
Parameters
529+
----------
530+
buf : bytearray or memoryview
531+
Read data into this buffer.
532+
533+
Returns
534+
-------
535+
int
536+
Number of bytes read and stored into `buf`.
537+
"""
538+
nbytes = len(buf)
539+
data = self._read_with_timeout(nbytes)
540+
541+
if data is None:
542+
return 0
543+
else:
544+
nbuf = len(data)
545+
546+
for i, c in zip(range(nbuf), data):
547+
buf[i] = c
548+
549+
return nbuf
550+
551+
def readline(self) -> Optional[bytes]:
552+
"""Read a line, ending in a newline character.
553+
554+
return None if a timeout occurs sooner, or return everything readable
555+
if no newline is found within timeout.
556+
557+
Returns
558+
-------
559+
bytes or None
560+
Data read.
561+
"""
562+
return self._read_with_timeout(None, line=True)
563+
564+
def write(self, buf: ReadableBuffer) -> int:
565+
"""Write the buffer of bytes to the bus.
566+
567+
Parameters
568+
----------
569+
buf : bytes or bytearray or memoryview
570+
Write out the char in this buffer.
571+
572+
Returns
573+
-------
574+
int
575+
Number of bytes written.
576+
"""
577+
written = 0
578+
579+
for msb, lsb in zip_longest(buf[1::2], buf[::2]):
580+
if msb is not None:
581+
self._write_int((msb << 8) | lsb)
582+
written += 2
583+
else:
584+
self._write_byte(lsb)
585+
written += 1
586+
587+
return written

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy