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)