|
31 | 31 | from typing import List, Union
|
32 | 32 |
|
33 | 33 | from pslab.bus.i2c import _I2CPrimitive
|
| 34 | +from pslab.bus.spi import _SPIPrimitive |
34 | 35 | from pslab.serial_handler import SerialHandler
|
35 | 36 |
|
36 | 37 | __all__ = "I2C"
|
@@ -178,3 +179,197 @@ def writeto_then_readfrom(
|
178 | 179 | self._restart(address, 1)
|
179 | 180 | buffer_in[in_start:in_end] = self._read(bytes_to_read)
|
180 | 181 | self._stop()
|
| 182 | + |
| 183 | + |
| 184 | +class SPI(_SPIPrimitive): |
| 185 | + """Busio SPI Class for CircuitPython Compatibility. |
| 186 | +
|
| 187 | + Parameters |
| 188 | + ---------- |
| 189 | + device : :class:`SerialHandler`, optional |
| 190 | + Serial connection to PSLab device. If not provided, a new one will be |
| 191 | + created. |
| 192 | + """ |
| 193 | + |
| 194 | + def __init__(self, device: SerialHandler = None, frequency: int = 125e3): |
| 195 | + super().__init__(device) |
| 196 | + ppre, spre = self._get_prescaler(25e4) |
| 197 | + self._set_parameters(ppre, spre, 1, 0, 1) |
| 198 | + self._bits = 8 |
| 199 | + |
| 200 | + @property |
| 201 | + def frequency(self) -> int: |
| 202 | + """Get the actual SPI bus frequency (rounded). |
| 203 | +
|
| 204 | + This may not match the frequency requested due to internal limitations. |
| 205 | + """ |
| 206 | + return round(self._frequency) |
| 207 | + |
| 208 | + def deinit(self) -> None: |
| 209 | + """Just a dummy method.""" |
| 210 | + pass |
| 211 | + |
| 212 | + def __enter__(self): |
| 213 | + """Just a dummy context manager.""" |
| 214 | + return self |
| 215 | + |
| 216 | + def __exit__(self, exc_type, exc_val, exc_tb) -> None: |
| 217 | + """Call :meth:`deinit` on context exit.""" |
| 218 | + self.deinit() |
| 219 | + |
| 220 | + def configure( |
| 221 | + self, |
| 222 | + *, |
| 223 | + baudrate: int = 100000, |
| 224 | + polarity: int = 0, |
| 225 | + phase: int = 0, |
| 226 | + bits: int = 8, |
| 227 | + ) -> None: |
| 228 | + """Configure the SPI bus. |
| 229 | +
|
| 230 | + Parameters |
| 231 | + ---------- |
| 232 | + baudrate : int |
| 233 | + The desired clock rate in Hertz. The actual clock rate may be |
| 234 | + higher or lower due to the granularity of available clock settings. |
| 235 | + Check the frequency attribute for the actual clock rate. |
| 236 | + polarity : int |
| 237 | + The base state of the clock line (0 or 1) |
| 238 | + phase : int |
| 239 | + The edge of the clock that data is captured. First (0) or second (1). |
| 240 | + Rising or falling depends on clock polarity. |
| 241 | + bits : int |
| 242 | + The number of bits per word. |
| 243 | + """ |
| 244 | + if polarity not in (0, 1): |
| 245 | + raise ValueError("Invalid polarity") |
| 246 | + if phase not in (0, 1): |
| 247 | + raise ValueError("Invalid phase") |
| 248 | + if bits not in self._INTEGER_TYPE_MAP: |
| 249 | + raise ValueError("Invalid number of bits") |
| 250 | + |
| 251 | + ppre, spre = self._get_prescaler(baudrate) |
| 252 | + cke = (phase ^ 1) & 1 |
| 253 | + self._set_parameters(ppre, spre, cke, polarity, 1) |
| 254 | + self._bits = bits |
| 255 | + |
| 256 | + def try_lock(self) -> bool: # pylint: disable=no-self-use |
| 257 | + """Just a dummy method.""" |
| 258 | + return True |
| 259 | + |
| 260 | + def unlock(self) -> None: |
| 261 | + """Just a dummy method.""" |
| 262 | + pass |
| 263 | + |
| 264 | + def write( |
| 265 | + self, |
| 266 | + buffer: Union[ReadableBuffer, List[int]], |
| 267 | + *, |
| 268 | + start: int = 0, |
| 269 | + end: int = None, |
| 270 | + ) -> None: |
| 271 | + """Write the data contained in buffer. If the buffer is empty, nothing happens. |
| 272 | +
|
| 273 | + Parameters |
| 274 | + ---------- |
| 275 | + buffer : bytes or bytearray or memoryview or list_of_int (for bits >8) |
| 276 | + Write out the data in this buffer. |
| 277 | + start : int |
| 278 | + Start of the slice of `buffer` to write out: `buffer[start:end]`. |
| 279 | + end : int |
| 280 | + End of the slice; this index is not included. Defaults to `len(buffer)`. |
| 281 | + """ |
| 282 | + end = len(buffer) if end is None else end |
| 283 | + buffer = buffer[start:end] |
| 284 | + |
| 285 | + if not buffer: |
| 286 | + return |
| 287 | + |
| 288 | + self._start() |
| 289 | + self._write_bulk(buffer, self._bits) |
| 290 | + self._stop() |
| 291 | + |
| 292 | + def readinto( |
| 293 | + self, |
| 294 | + buffer: Union[WriteableBuffer, List[int]], |
| 295 | + *, |
| 296 | + start: int = 0, |
| 297 | + end: int = None, |
| 298 | + write_value: int = 0, |
| 299 | + ) -> None: |
| 300 | + """Read into `buffer` while writing `write_value` for each byte read. |
| 301 | +
|
| 302 | + If the number of bytes to read is 0, nothing happens. |
| 303 | +
|
| 304 | + Parameters |
| 305 | + ---------- |
| 306 | + buffer : bytearray or memoryview or list_of_int (for bits >8) |
| 307 | + Read data into this buffer. |
| 308 | + start : int |
| 309 | + Start of the slice of `buffer` to read into: `buffer[start:end]`. |
| 310 | + end : int |
| 311 | + End of the slice; this index is not included. Defaults to `len(buffer)`. |
| 312 | + write_value : int |
| 313 | + Value to write while reading. (Usually ignored.) |
| 314 | + """ |
| 315 | + end = len(buffer) if end is None else end |
| 316 | + bytes_to_read = end - start |
| 317 | + |
| 318 | + if bytes_to_read == 0: |
| 319 | + return |
| 320 | + |
| 321 | + self._start() |
| 322 | + data = self._transfer_bulk([write_value] * bytes_to_read, self._bits) |
| 323 | + self._stop() |
| 324 | + |
| 325 | + for i, v in zip(range(start, end), data): |
| 326 | + buffer[i] = v |
| 327 | + |
| 328 | + def write_readinto( |
| 329 | + self, |
| 330 | + buffer_out: Union[ReadableBuffer, List[int]], |
| 331 | + buffer_in: Union[WriteableBuffer, List[int]], |
| 332 | + *, |
| 333 | + out_start: int = 0, |
| 334 | + out_end: int = None, |
| 335 | + in_start: int = 0, |
| 336 | + in_end: int = None, |
| 337 | + ): |
| 338 | + """Write out the data in buffer_out while simultaneously read into buffer_in. |
| 339 | +
|
| 340 | + The lengths of the slices defined by buffer_out[out_start:out_end] and |
| 341 | + buffer_in[in_start:in_end] must be equal. If buffer slice lengths are both 0, |
| 342 | + nothing happens. |
| 343 | +
|
| 344 | + Parameters |
| 345 | + ---------- |
| 346 | + buffer_out : bytes or bytearray or memoryview or list_of_int (for bits >8) |
| 347 | + Write out the data in this buffer. |
| 348 | + buffer_in : bytearray or memoryview or list_of_int (for bits >8) |
| 349 | + Read data into this buffer. |
| 350 | + out_start : int |
| 351 | + Start of the slice of `buffer_out` to write out: |
| 352 | + `buffer_out[out_start:out_end]`. |
| 353 | + out_end : int |
| 354 | + End of the slice; this index is not included. Defaults to `len(buffer_out)` |
| 355 | + in_start : int |
| 356 | + Start of the slice of `buffer_in` to read into:`buffer_in[in_start:in_end]` |
| 357 | + in_end : int |
| 358 | + End of the slice; this index is not included. Defaults to `len(buffer_in)` |
| 359 | + """ |
| 360 | + out_end = len(buffer_out) if out_end is None else out_end |
| 361 | + in_end = len(buffer_in) if in_end is None else in_end |
| 362 | + buffer_out = buffer_out[out_start:out_end] |
| 363 | + bytes_to_read = in_end - in_start |
| 364 | + |
| 365 | + if len(buffer_out) != bytes_to_read: |
| 366 | + raise ValueError("buffer slices must be of equal length") |
| 367 | + if bytes_to_read == 0: |
| 368 | + return |
| 369 | + |
| 370 | + self._start() |
| 371 | + data = self._transfer_bulk(buffer_out, self._bits) |
| 372 | + self._stop() |
| 373 | + |
| 374 | + for i, v in zip(range(in_start, in_end), data): |
| 375 | + buffer_in[i] = v |
0 commit comments