Skip to content

Commit 44e8de6

Browse files
committed
Initial working version of I2C
1 parent 1bb5361 commit 44e8de6

File tree

1 file changed

+193
-1
lines changed

1 file changed

+193
-1
lines changed

adafruit_bitbangio.py

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"""
4242

4343
# imports
44-
from time import monotonic
44+
from time import monotonic, sleep
4545
from digitalio import DigitalInOut
4646

4747
__version__ = "0.0.0-auto.0"
@@ -84,6 +84,198 @@ def deinit(self):
8484

8585
# pylint: enable=no-self-use
8686

87+
class I2C(_BitBangIO):
88+
"""Software-based implementation of the I2C protocol over GPIO pins."""
89+
def __init__(self, scl, sda, *, frequency=400000, timeout=1):
90+
"""Initialize bitbang (or software) based I2C. Must provide the I2C
91+
clock, and data pin numbers.
92+
"""
93+
super().__init__()
94+
95+
# Set pins as outputs/inputs.
96+
self._scl = DigitalInOut(scl)
97+
self._scl.switch_to_output()
98+
self._scl.value = 1
99+
100+
# SDA flips between being input and output
101+
self._sda = DigitalInOut(sda)
102+
self._sda.switch_to_output()
103+
self._sda.value = 1
104+
105+
self._delay = 1 / frequency / 2
106+
self._frequency = frequency
107+
self._timeout = timeout
108+
109+
def scan(self):
110+
"""Perform an I2C Device Scan"""
111+
found = []
112+
for address in range(0, 0x80):
113+
if self._probe(address):
114+
found.append(address)
115+
return found
116+
117+
def writeto(self, address, buffer, *, start=0, end=None, stop=True):
118+
"""Write data from the buffer to an address"""
119+
if end is None:
120+
end = len(buffer)
121+
self._write(address, buffer[start:end], stop)
122+
123+
def readfrom_into(self, address, buffer, *, start=0, end=None):
124+
"""Read data from an address and into the buffer"""
125+
if end is None:
126+
end = len(buffer)
127+
128+
readin = self._read(address, end - start)
129+
for i in range(end - start):
130+
buffer[i + start] = readin[i]
131+
132+
def writeto_then_readfrom(
133+
self,
134+
address,
135+
out_buffer,
136+
in_buffer,
137+
*,
138+
out_start=0,
139+
out_end=None,
140+
in_start=0,
141+
in_end=None
142+
):
143+
"""Write data from buffer_out to an address and then
144+
read data from an address and into buffer_in
145+
"""
146+
if out_end is None:
147+
out_end = len(buffer_out)
148+
if in_end is None:
149+
in_end = len(buffer_in)
150+
self.writeto(address, buffer_out, start=out_start, end=out_end, stop=stop)
151+
self.readfrom_into(address, buffer_in, start=in_start, end=in_end)
152+
153+
def _scl_low(self):
154+
self._scl.value = 0
155+
156+
def _sda_low(self):
157+
self._sda.value = 0
158+
159+
def _scl_release(self):
160+
"""Release and let the pullups lift"""
161+
self._scl.value = 1
162+
163+
def _sda_release(self):
164+
"""Release and let the pullups lift"""
165+
self._sda.value = 1
166+
167+
def _set_values(self, *, scl, sda, delay=None):
168+
if delay is None:
169+
delay = self._delay
170+
self._scl.value = scl
171+
self._scl.value = sda
172+
sleep(delay)
173+
174+
def _start(self):
175+
self._sda_release()
176+
self._scl_release()
177+
sleep(self._delay)
178+
self._sda_low()
179+
sleep(self._delay)
180+
181+
def _stop(self):
182+
self._scl_low()
183+
sleep(self._delay)
184+
self._sda_low()
185+
sleep(self._delay)
186+
self._scl_release()
187+
sleep(self._delay)
188+
self._sda_release()
189+
sleep(self._delay)
190+
191+
def _repeated_start(self):
192+
self._scl_low()
193+
sleep(self._delay)
194+
self._sda_release()
195+
sleep(self._delay)
196+
self._scl_release()
197+
sleep(self._delay)
198+
self._sda_low()
199+
sleep(self._delay)
200+
201+
def _write_byte(self, byte):
202+
for bit_position in range(8):
203+
self._scl_low()
204+
sleep(self._delay)
205+
if byte & (0x80 >> bit_position):
206+
self._sda_release()
207+
else:
208+
self._sda_low()
209+
sleep(self._delay)
210+
self._scl_release()
211+
sleep(self._delay)
212+
self._scl_low()
213+
sleep(self._delay * 2)
214+
215+
self._scl_release()
216+
sleep(self._delay)
217+
218+
self._sda.switch_to_input()
219+
ack = self._sda.value
220+
self._sda.switch_to_output()
221+
sleep(self._delay)
222+
223+
self._scl_low()
224+
225+
return not ack
226+
227+
def _read_byte(self, ack=False):
228+
self._scl_low()
229+
sleep(self._delay)
230+
231+
data = 0
232+
self._sda.switch_to_input()
233+
for _ in range(8):
234+
self._scl_release()
235+
sleep(self._delay)
236+
data = (data << 1) | int(self._sda.value)
237+
sleep(self._delay)
238+
self._scl_low()
239+
sleep(self._delay)
240+
self._sda.switch_to_output()
241+
242+
if ack:
243+
self._sda_low()
244+
else:
245+
self._sda_release()
246+
sleep(self._delay)
247+
self._scl_release()
248+
sleep(self._delay)
249+
return data & 0xFF
250+
251+
def _probe(self, address):
252+
self._start()
253+
ok = self._write_byte(address << 1)
254+
self._stop()
255+
return ok > 0
256+
257+
def _write(self, address, buffer, transmit_stop):
258+
self._start()
259+
if not self._write_byte(address << 1):
260+
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
261+
for byte in buffer:
262+
self._write_byte(byte)
263+
if transmit_stop:
264+
self._stop()
265+
266+
def _read(self, address, length):
267+
self._start()
268+
if not self._write_byte(address << 1 | 1):
269+
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
270+
buffer = bytearray(length)
271+
for byte_position in range(length):
272+
buffer[byte_position] = self._read_byte(ack=(byte_position != length - 1))
273+
self._stop()
274+
return buffer
275+
276+
def _read_i2c_block_data(self, address, write_bytes, length):
277+
"""Untested"""
278+
pass
87279

88280
class SPI(_BitBangIO):
89281
"""Software-based implementation of the SPI protocol over GPIO pins."""

0 commit comments

Comments
 (0)