Skip to content

Commit 04780e8

Browse files
nkpro2000srbessman
authored andcommitted
Add busio.I2C
1 parent 671838c commit 04780e8

File tree

2 files changed

+223
-26
lines changed

2 files changed

+223
-26
lines changed

pslab/bus/busio.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
"""Circuitpython's busio compatibility layer for pslab-python.
2+
3+
This module emulates the CircuitPython's busio API for devices or hosts running
4+
CPython or MicroPython using pslab-python.
5+
This helps to access many sensors using Adafruit's drivers via PSLab board.
6+
7+
Notes
8+
-----
9+
Documentation:
10+
https://circuitpython.readthedocs.io/en/6.3.x/shared-bindings/busio/index.html
11+
12+
Examples
13+
--------
14+
Get humidity from Si7021 temperature and humidity sensor using adafruit_si7021 module.
15+
16+
>>> import adafruit_si7021
17+
>>> from pslab.bus import busio # import board, busio
18+
>>> i2c = busio.I2C() # i2c = busio.I2C(board.SCL, board.SDA)
19+
>>> sensor = adafruit_si7021.SI7021(i2c)
20+
>>> print(sensor.relative_humidity)
21+
22+
Get gyro reading from BNO055 using adafruit_bno055, board(just a wrapper for busio).
23+
24+
>>> import adafruit_bno055
25+
>>> from pslab.bus import busio # import board
26+
>>> i2c = busio.I2C() # i2c = board.I2C()
27+
>>> sensor = adafruit_bno055.BNO055_I2C(i2c)
28+
>>> print(sensor.gyro)
29+
"""
30+
from typing import List, Union
31+
32+
from pslab.bus.i2c import I2CPrimitive, I2CSlave
33+
from pslab.serial_handler import SerialHandler
34+
35+
__all__ = "I2C"
36+
ReadableBuffer = Union[bytes, bytearray, memoryview]
37+
WriteableBuffer = Union[bytearray, memoryview]
38+
39+
40+
class I2C(I2CPrimitive):
41+
"""Busio I2C Class for CircuitPython Compatibility.
42+
43+
Parameters
44+
----------
45+
device : :class:`SerialHandler`, optional
46+
Serial connection to PSLab device. If not provided, a new one will be
47+
created.
48+
frequency : float, optional
49+
Frequency of SCL in Hz.
50+
"""
51+
52+
def __init__(self, device: SerialHandler = None, frequency: int = 125e3):
53+
# 125 kHz is as low as the PSLab can go.
54+
super().__init__(device)
55+
self._init()
56+
self._configure(frequency)
57+
58+
def deinit(self) -> None:
59+
"""Just a dummy method."""
60+
pass
61+
62+
def __enter__(self):
63+
"""Just a dummy method."""
64+
return self
65+
66+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
67+
"""Just a dummy method."""
68+
self.deinit()
69+
70+
def scan(self) -> List[int]:
71+
"""Scan all I2C addresses between 0x08 and 0x77 inclusive.
72+
73+
Returns
74+
-------
75+
addrs : list of int
76+
List of 7-bit addresses on which slave devices replied.
77+
"""
78+
addrs = []
79+
80+
for address in range(0x08, 0x77):
81+
slave = I2CSlave(address, self._device)
82+
83+
if slave.ping():
84+
addrs.append(address)
85+
86+
return addrs
87+
88+
def try_lock(self) -> bool: # pylint: disable=no-self-use
89+
"""Just a dummy method."""
90+
return True
91+
92+
def unlock(self) -> None:
93+
"""Just a dummy method."""
94+
pass
95+
96+
def readfrom_into(
97+
self, address: int, buffer: WriteableBuffer, *, start: int = 0, end: int = None
98+
) -> None:
99+
"""Read from a device at specified address into a buffer.
100+
101+
Parameters
102+
----------
103+
address : int
104+
7-bit I2C device address.
105+
buffer : bytearray or memoryview
106+
buffer to write into.
107+
start : int
108+
Index to start writing at.
109+
end : int
110+
Index to write up to but not include. Defaults to length of `buffer`.
111+
"""
112+
end = len(buffer) if end is None else end
113+
bytes_to_read = end - start
114+
self._start(address, 1)
115+
buffer[start:end] = self._read(bytes_to_read)
116+
self._stop()
117+
118+
def writeto(
119+
self,
120+
address: int,
121+
buffer: ReadableBuffer,
122+
*,
123+
start: int = 0,
124+
end: int = None,
125+
stop: bool = True,
126+
) -> None:
127+
"""Write to a device at specified address from a buffer.
128+
129+
Parameters
130+
----------
131+
address : int
132+
7-bit I2C device address.
133+
buffer : bytes or bytearray or memoryview
134+
buffer containing the bytes to write.
135+
start : int
136+
Index to start writing from.
137+
end : int
138+
Index to read up to but not include. Defaults to length of `buffer`.
139+
stop : bool
140+
Enable to transmit a stop bit. Defaults to True.
141+
"""
142+
end = len(buffer) if end is None else end
143+
144+
if stop:
145+
self._write_bulk(address, buffer[start:end])
146+
else:
147+
self._start(address, 0)
148+
self._send(buffer[start:end])
149+
150+
def writeto_then_readfrom(
151+
self,
152+
address: int,
153+
buffer_out: ReadableBuffer,
154+
buffer_in: WriteableBuffer,
155+
*,
156+
out_start: int = 0,
157+
out_end: int = None,
158+
in_start: int = 0,
159+
in_end: int = None,
160+
):
161+
"""Write to then read from a device at specified address.
162+
163+
Parameters
164+
----------
165+
address : int
166+
7-bit I2C device address.
167+
out_buffer : bytes or bytearray or memoryview
168+
buffer containing the bytes to write.
169+
in_buffer : bytearray or memoryview
170+
buffer to write into.
171+
out_start : int
172+
Index to start writing from.
173+
out_end : int
174+
Index to read up to but not include. Defaults to length of `out_buffer`.
175+
in_start : int
176+
Index to start writing at.
177+
in_end : int
178+
Index to write up to but not include. Defaults to length of `in_buffer`.
179+
"""
180+
out_end = len(buffer_out) if out_end is None else out_end
181+
in_end = len(buffer_in) if in_end is None else in_end
182+
bytes_to_read = in_end - in_start
183+
self._start(address, 0)
184+
self._send(buffer_out[out_start:out_end])
185+
self._restart(address, 1)
186+
buffer_in[in_start:in_end] = self._read(bytes_to_read)
187+
self._stop()

pslab/bus/i2c.py

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
from pslab.serial_handler import SerialHandler
2525
from pslab.external.sensorlist import sensors
2626

27+
__all__ = (
28+
"I2CMaster",
29+
"I2CSlave",
30+
)
2731
logger = logging.getLogger(__name__)
2832

2933

@@ -42,6 +46,9 @@ class I2CPrimitive:
4246
_MIN_BRGVAL = 2
4347
_MAX_BRGVAL = 511
4448

49+
# Specs say typical delay is 110 ns to 130 ns; 150 ns from testing.
50+
_SCL_DELAY = 150e-9
51+
4552
_ACK = 0
4653
_READ = 1
4754
_WRITE = 0
@@ -56,23 +63,40 @@ def _init(self):
5663
self._device.send_byte(CP.I2C_INIT)
5764
self._device.get_ack()
5865

59-
def _configure(self, brgval: int):
60-
"""Configure bus brgval.
66+
def _configure(self, frequency: float):
67+
"""Configure bus frequency.
6168
6269
Parameters
6370
----------
64-
brgval : int
65-
Brgval of SCL in Hz.
71+
frequency : float
72+
Frequency of SCL in Hz.
73+
74+
Raises
75+
------
76+
ValueError
77+
If given frequency is not supported by PSLab board.
6678
"""
79+
brgval = self._get_i2c_brgval(frequency)
80+
6781
if self._MIN_BRGVAL <= brgval <= self._MAX_BRGVAL:
6882
self._device.send_byte(CP.I2C_HEADER)
6983
self._device.send_byte(CP.I2C_CONFIG)
7084
self._device.send_int(brgval)
7185
self._device.get_ack()
7286
else:
73-
e = f"Brgval must be between {self._MIN_BRGVAL} and {self._MAX_BRGVAL}."
87+
min_frequency = self._get_i2c_frequency(self._MAX_BRGVAL)
88+
max_frequency = self._get_i2c_frequency(self._MIN_BRGVAL)
89+
e = f"Frequency must be between {min_frequency} and {max_frequency} Hz."
7490
raise ValueError(e)
7591

92+
@classmethod
93+
def _get_i2c_brgval(cls, frequency: float) -> int:
94+
return int((1 / frequency - cls._SCL_DELAY) * CP.CLOCK_RATE - 2)
95+
96+
@classmethod
97+
def _get_i2c_frequency(cls, brgval: int) -> float:
98+
return 1 / ((brgval + 2) / CP.CLOCK_RATE + cls._SCL_DELAY)
99+
76100
def _start(self, address: int, mode: int) -> int:
77101
"""Initiate I2C transfer.
78102
@@ -390,9 +414,6 @@ class I2CMaster(I2CPrimitive):
390414
created.
391415
"""
392416

393-
# Specs say typical delay is 110 ns to 130 ns; 150 ns from testing.
394-
_SCL_DELAY = 150e-9
395-
396417
def __init__(self, device: SerialHandler = None):
397418
super().__init__(device)
398419
self._init()
@@ -405,24 +426,13 @@ def configure(self, frequency: float):
405426
----------
406427
frequency : float
407428
Frequency of SCL in Hz.
408-
"""
409-
brgval = self._get_i2c_brgval(frequency)
410-
411-
if self._MIN_BRGVAL <= brgval <= self._MAX_BRGVAL:
412-
self._configure(brgval)
413-
else:
414-
min_frequency = self._get_i2c_frequency(self._MAX_BRGVAL)
415-
max_frequency = self._get_i2c_frequency(self._MIN_BRGVAL)
416-
e = f"Frequency must be between {min_frequency} and {max_frequency} Hz."
417-
raise ValueError(e)
418-
419-
@classmethod
420-
def _get_i2c_brgval(cls, frequency: float) -> int:
421-
return int((1 / frequency - cls._SCL_DELAY) * CP.CLOCK_RATE - 2)
422429
423-
@classmethod
424-
def _get_i2c_frequency(cls, brgval: int) -> float:
425-
return 1 / ((brgval + 2) / CP.CLOCK_RATE + cls._SCL_DELAY)
430+
Raises
431+
------
432+
ValueError
433+
If given frequency is not supported by PSLab board.
434+
"""
435+
self._configure(frequency)
426436

427437
def scan(self) -> List[int]:
428438
"""Scan I2C port for connected devices.
@@ -575,7 +585,7 @@ def write(self, bytes_to_write: bytearray, register_address: int = 0x0):
575585
----------
576586
bytes_to_write : bytearray
577587
Data to write to the slave.
578-
register_address : int
588+
register_address : int, optional
579589
Slave device internal memory address to write to. The default
580590
value is 0x0.
581591
"""

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