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