Skip to content

Commit e9180c5

Browse files
nkpro2000srbessman
authored andcommitted
Refactor UART
1 parent 4a5e88c commit e9180c5

File tree

3 files changed

+313
-52
lines changed

3 files changed

+313
-52
lines changed

pslab/bus/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
"""Contains modules for interfacing with the PSLab's I2C, SPI, and UART buses.
2-
3-
(UART still TODO)
4-
"""
1+
"""Contains modules for interfacing with the PSLab's I2C, SPI, and UART buses."""
52

63
from pslab.bus.i2c import I2CMaster, I2CSlave
74
from pslab.bus.spi import SPIMaster, SPISlave
5+
from pslab.bus.uart import UART
86

97
__all__ = (
108
"I2CMaster",
119
"I2CSlave",
1210
"SPIMaster",
1311
"SPISlave",
12+
"UART",
1413
)

pslab/bus/uart.py

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
"""Control the PSLab's UART bus and devices connected on the bus.
2+
3+
Examples
4+
--------
5+
Set UART2 bus baudrate to 1000000:
6+
7+
>>> from pslab.bus.uart improt UART
8+
>>> bus = UART()
9+
>>> bus.configure(1e6)
10+
11+
Send a byte over UART:
12+
13+
>>> bus.write_byte(0x55)
14+
"""
15+
16+
import sys
17+
from typing import Tuple
18+
19+
import pslab.protocol as CP
20+
from pslab.serial_handler import SerialHandler
21+
22+
__all__ = "UART"
23+
_BRGVAL = 0x22 # BaudRate = 460800.
24+
_MODE = (0, 0) # 8-bit data and no parity, 1 stop bit.
25+
26+
27+
class classmethod_(classmethod):
28+
"""Support chaining classmethod and property."""
29+
30+
def __init__(self, f):
31+
self.f = f
32+
super().__init__(f)
33+
34+
def __get__(self, obj, cls=None):
35+
"""Check python version and return appropriate getter."""
36+
# classmethod() to support chained decorators; new in python 3.9.
37+
if sys.version_info < (3, 9) and isinstance(self.f, property):
38+
return self.f.__get__(cls)
39+
else:
40+
return super().__get__(obj, cls)
41+
42+
43+
class _UARTPrimitive:
44+
"""UART primitive commands.
45+
46+
Handles all the UART subcommands coded in pslab-firmware.
47+
48+
Parameters
49+
----------
50+
device : :class:`SerialHandler`, optional
51+
Serial connection to PSLab device. If not provided, a new one will be created.
52+
"""
53+
54+
_MIN_BRGVAL = 0
55+
_MAX_BRGVAL = 2 ** 16 - 1
56+
57+
_brgval = _BRGVAL
58+
_mode = _MODE
59+
60+
def __init__(self, device: SerialHandler = None):
61+
self._device = device if device is not None else SerialHandler()
62+
63+
@classmethod_
64+
@property
65+
def _baudrate(cls) -> float:
66+
return cls._get_uart_baudrate(cls._brgval)
67+
68+
@staticmethod
69+
def _get_uart_brgval(baudrate: float, BRGH: int = 1) -> int:
70+
return round(((CP.CLOCK_RATE / baudrate) / (4 if BRGH else 16)) - 1)
71+
72+
@staticmethod
73+
def _get_uart_baudrate(brgval: int, BRGH: int = 1) -> float:
74+
return (CP.CLOCK_RATE / (brgval + 1)) / (4 if BRGH else 16)
75+
76+
@staticmethod
77+
def _save_config(brgval: int = None, mode: Tuple[int] = None):
78+
"""Save the UART barval and mode bits.
79+
80+
Parameters
81+
----------
82+
brgval : int, optional
83+
Set value to `_UARTPrimitive._brgval`. Will be skipped if None.
84+
Defaults to None.
85+
mode : tuple of int, optional
86+
Set value to `_UARTPrimitive._mode`. Will be skipped if None.
87+
Defaults to None.
88+
"""
89+
if brgval is not None:
90+
_UARTPrimitive._brgval = brgval
91+
if mode is not None:
92+
_UARTPrimitive._mode = mode
93+
94+
def _set_uart_baud(self, baudrate: int):
95+
"""Set the baudrate of the UART bus.
96+
97+
It is a primitive UART method, prefered to use :meth:`UART.configure`.
98+
99+
Parameters
100+
----------
101+
baudrate : int
102+
Baudrate to set on the UART bus.
103+
104+
Raises
105+
------
106+
ValueError
107+
If given baudrate in not supported by PSLab board.
108+
"""
109+
brgval = self._get_uart_brgval(baudrate)
110+
111+
if self._MIN_BRGVAL <= brgval <= self._MAX_BRGVAL:
112+
self._device.send_byte(CP.UART_2)
113+
self._device.send_byte(CP.SET_BAUD)
114+
self._device.send_int(brgval)
115+
self._device.get_ack()
116+
self._save_config(brgval=brgval)
117+
else:
118+
min_baudrate = self._get_uart_baudrate(self._MIN_BRGVAL)
119+
max_baudrate = self._get_uart_baudrate(self._MAX_BRGVAL)
120+
e = f"Baudrate must be between {min_baudrate} and {max_baudrate}."
121+
raise ValueError(e)
122+
123+
def _set_uart_mode(self, pd: int, st: int):
124+
"""Set UART mode.
125+
126+
Parameters
127+
----------
128+
pd : {0, 1, 2, 3}
129+
Parity and data selection bits.
130+
{0: 8-bit data and no parity,
131+
1: 8-bit data and even parity,
132+
2: 8-bit data and odd parity,
133+
3: 9-bit data and no parity}
134+
st : {0, 1}
135+
Selects number of stop bits for each one-byte UART transmission.
136+
{0: one stop bit,
137+
1: two stop bits}
138+
139+
Raises
140+
------
141+
ValueError
142+
If any one of arguments is not in its shown range.
143+
RuntimeError
144+
If this functionality is not supported by the firmware.
145+
Since it is newly implemented, earlier firmware version don't support.
146+
"""
147+
error_message = []
148+
if pd not in range(0, 4):
149+
error_message.append("Parity and data selection bits must be 2-bits.")
150+
if st not in (0, 1):
151+
error_message.append("Stop bits select must be a bit.")
152+
153+
self._device.send_byte(CP.UART_2)
154+
self._device.send_byte(CP.SET_MODE)
155+
156+
# Verifying whether the firmware support current subcommand.
157+
if self._device.get_byte() == 4:
158+
self._device.send_byte((pd << 1) | st)
159+
self._device.get_ack()
160+
self._save_config(mode=(pd, st))
161+
else:
162+
raise RuntimeError(
163+
"This firmware version doesn't support this functionality."
164+
)
165+
166+
def _read_uart_status(self) -> int:
167+
"""Return whether receive buffer has data.
168+
169+
Returns
170+
-------
171+
status : int
172+
1 if at least one more character can be read else 0.
173+
"""
174+
self._device.send_byte(CP.UART_2)
175+
self._device.send_byte(CP.READ_UART2_STATUS)
176+
return self._device.get_byte()
177+
178+
def _write_byte(self, data: int):
179+
"""Write a single byte to the UART bus.
180+
181+
It is a primitive UART method, prefered to use :meth:`UART.write_byte`.
182+
183+
Parameters
184+
----------
185+
data : int
186+
Byte value to write to the UART bus.
187+
"""
188+
self._device.send_byte(CP.UART_2)
189+
self._device.send_byte(CP.SEND_BYTE)
190+
self._device.send_byte(data)
191+
self._device.get_ack()
192+
193+
def _write_int(self, data: int):
194+
"""Write a single int to the UART bus.
195+
196+
It is a primitive UART method, prefered to use :meth:`UART.write_int`.
197+
198+
Parameters
199+
----------
200+
data : int
201+
Int value to write to the UART bus.
202+
"""
203+
self._device.send_byte(CP.UART_2)
204+
self._device.send_byte(CP.SEND_INT)
205+
self._device.send_int(data)
206+
self._device.get_ack()
207+
208+
def _read_byte(self) -> int:
209+
"""Read a single byte from the UART bus.
210+
211+
It is a primitive UART method, prefered to use :meth:`UART.read_byte`.
212+
213+
Returns
214+
-------
215+
data : int
216+
A Byte interpreted as a uint8 read from the UART bus.
217+
"""
218+
self._device.send_byte(CP.UART_2)
219+
self._device.send_byte(CP.READ_BYTE)
220+
return self._device.get_byte()
221+
222+
def _read_int(self) -> int:
223+
"""Read a two byte value from the UART bus.
224+
225+
It is a primitive UART method, prefered to use :meth:`UART.read_int`.
226+
227+
Returns
228+
-------
229+
data : int
230+
Two bytes interpreted as a uint16 read from the UART bus.
231+
"""
232+
self._device.send_byte(CP.UART_2)
233+
self._device.send_byte(CP.READ_INT)
234+
return self._device.get_int()
235+
236+
237+
class UART(_UARTPrimitive):
238+
"""UART2 bus.
239+
240+
Parameters
241+
----------
242+
device : :class:`SerialHandler`, optional
243+
Serial connection to PSLab device. If not provided, a new one will be created.
244+
"""
245+
246+
def __init__(self, device: SerialHandler = None):
247+
super().__init__(device)
248+
# Reset baudrate and mode
249+
self.configure(self._get_uart_baudrate(_BRGVAL))
250+
251+
try:
252+
self._set_uart_mode(*_MODE)
253+
except RuntimeError:
254+
pass
255+
256+
def configure(self, baudrate: float):
257+
"""Configure UART bus baudrate.
258+
259+
Parameters
260+
----------
261+
baudrate : float
262+
263+
Raises
264+
------
265+
ValueError
266+
If given baudrate is not supported by PSLab board.
267+
"""
268+
self._set_uart_baud(baudrate)
269+
270+
def write_byte(self, data: int):
271+
"""Write a single byte to the UART bus.
272+
273+
Parameters
274+
----------
275+
data : int
276+
Byte value to write to the UART bus.
277+
"""
278+
self._write_byte(data)
279+
280+
def write_int(self, data: int):
281+
"""Write a single int to the UART bus.
282+
283+
Parameters
284+
----------
285+
data : int
286+
Int value to write to the UART bus.
287+
"""
288+
self._write_int(data)
289+
290+
def read_byte(self) -> int:
291+
"""Read a single byte from the UART bus.
292+
293+
Returns
294+
-------
295+
data : int
296+
A Byte interpreted as a uint8 read from the UART bus.
297+
"""
298+
return self._read_byte()
299+
300+
def read_int(self) -> int:
301+
"""Read a two byte value from the UART bus.
302+
303+
It is a primitive UART method, prefered to use :meth:`UART.read_int`.
304+
305+
Returns
306+
-------
307+
data : int
308+
Two bytes interpreted as a uint16 read from the UART bus.
309+
"""
310+
return self._read_int()

pslab/sciencelab.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -285,54 +285,6 @@ def enable_uart_passthrough(self, baudrate: int, persist=False):
285285
self.send_byte(1 if persist else 0)
286286
self.send_int(int(round(((64e6 / baudrate) / 4) - 1)))
287287

288-
def set_uart_baud(self, baudrate: int):
289-
"""Set the baudrate of the UART bus.
290-
291-
Parameters
292-
----------
293-
baudrate : int
294-
Baudrate to set on the UART bus.
295-
"""
296-
self.send_byte(CP.UART_2)
297-
self.send_byte(CP.SET_BAUD)
298-
self.send_int(int(round(((64e6 / baudrate) / 4) - 1)))
299-
self.get_ack()
300-
301-
def write_uart(self, byte: int):
302-
"""Write a single byte to the UART bus.
303-
304-
Parameters
305-
----------
306-
byte : int
307-
Byte value to write to the UART bus.
308-
"""
309-
self.send_byte(CP.UART_2)
310-
self.send_byte(CP.SEND_BYTE)
311-
self.send_byte(byte)
312-
self.get_ack()
313-
314-
def read_uart(self):
315-
"""Read a single byte from the UART bus.
316-
317-
Returns
318-
-------
319-
byte : int
320-
Byte value read from the UART bus."""
321-
self.send_byte(CP.UART_2)
322-
self.send_byte(CP.READ_BYTE)
323-
return self.get_byte()
324-
325-
def read_uart_status(self):
326-
"""Return available bytes in UART buffer.
327-
328-
Returns
329-
-------
330-
status : int
331-
"""
332-
self.send_byte(CP.UART_2)
333-
self.send_byte(CP.READ_UART2_STATUS)
334-
return self.get_byte()
335-
336288
def read_log(self):
337289
"""Read hardware debug log.
338290

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