-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSoftwareI2C.py
199 lines (180 loc) · 6.53 KB
/
SoftwareI2C.py
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
from hub import pins as Pin
from hub import uart
import time
import struct
class SoftwareI2C:
SW_I2C_WAIT_TIME = 40 # Microseconds to wait, adjust based on your needs
def __init__(self, scl_pin, sda_pin):
self.en = Pin.init(0, Pin.EN, Pin.OUT)
self.en.value(1)
self.scl = Pin.init(0, scl_pin, Pin.OUT)
self.sda = Pin.init(0, sda_pin, Pin.OUT)
self.scl.value(1)
self.sda.value(1)
def scl_high(self):
self.scl.value(1)
def scl_low(self):
self.scl.value(0)
def sda_high(self):
self.sda.value(1)
def sda_low(self):
self.sda.value(0)
def sda_input(self):
self.sda = Pin.init(0, Pin.RX, Pin.IN)
def sda_output(self):
self.sda = Pin.init(0, Pin.RX, Pin.OUT)
def delay_us(self, us):
time.sleep_us(us)
def start(self):
self.sda_high()
self.scl_high()
self.delay_us(self.SW_I2C_WAIT_TIME)
self.sda_low()
self.delay_us(self.SW_I2C_WAIT_TIME)
self.scl_low()
self.delay_us(self.SW_I2C_WAIT_TIME * 2)
def stop(self):
self.sda_low()
self.scl_high()
self.delay_us(self.SW_I2C_WAIT_TIME)
self.sda_high()
self.delay_us(self.SW_I2C_WAIT_TIME)
def check_ack(self):
self.sda_input()
self.scl_high()
ack = not self.sda.value()
self.scl_low()
self.sda_output()
self.delay_us(self.SW_I2C_WAIT_TIME)
return ack
def write_byte(self, byte):
self.scl_low()
for i in range(8):
self.sda.value((byte >> (7 - i)) & 1)
self.delay_us(self.SW_I2C_WAIT_TIME)
self.scl_high()
self.delay_us(self.SW_I2C_WAIT_TIME)
self.scl_low()
return self.check_ack()
def read_byte(self, ack=True):
self.sda_input()
byte = 0
for i in range(8):
self.scl_high()
byte = (byte << 1) | self.sda.value()
self.scl_low()
self.sda_output()
if ack:
self.sda_low()
else:
self.sda_high()
self.scl_high()
self.delay_us(self.SW_I2C_WAIT_TIME)
self.scl_low()
self.sda_high()
return byte
def scan(self):
found_devices = []
for address in range(0x00, 0x78): # Valid I2C addresses
self.start()
#print("add")
#print(address)
if self.write_byte(address << 1): # Shift address for write mode
#print("found")
found_devices.append(address)
self.stop()
return found_devices
def writeto(i2c, address, data):
"""
Write data to an I2C device.
:param i2c: SoftwareI2C object instance.
:param address: 7-bit I2C device address.
:param data: Bytearray or list of data to write.
"""
i2c.start()
if i2c.write_byte(address << 1): # Shift address for write mode and send
for byte in data:
if not i2c.write_byte(byte):
print("Error: No ACK received after data byte.")
break
else:
print("Error: No ACK received for address.")
i2c.stop()
def readfrom(i2c, address, num_bytes):
"""
Read data from an I2C device.
:param i2c: SoftwareI2C object instance.
:param address: 7-bit I2C device address.
:param num_bytes: Number of bytes to read.
:return: Data read as a bytearray.
"""
data = bytearray()
i2c.start()
if i2c.write_byte((address << 1) | 1): # Shift address for read mode and send
for i in range(num_bytes):
ack = i < num_bytes - 1 # ACK all but the last byte
data.append(i2c.read_byte(ack))
else:
print("Error: No ACK received for address.")
i2c.stop()
return data
def writeto_mem(i2c, device_addr, register_addr, data):
"""
Write data to a specific register of an I2C device.
:param i2c: SoftwareI2C object instance, the custom I2C implementation.
:param device_addr: The 7-bit address of the I2C device.
:param register_addr: The register address within the I2C device where data will be written.
:param data: The data to write (as a bytes object or list of bytes).
"""
i2c.start() # Start I2C communication
# Send the device address in write mode
if not i2c.write_byte(device_addr << 1):
print("Error: No ACK received for device address.")
i2c.stop()
return
# Send the register address
if not i2c.write_byte(register_addr):
print("Error: No ACK received for register address.")
i2c.stop()
return
# Write the data bytes
for byte in data:
if not i2c.write_byte(byte):
print("Error: No ACK received after data byte.")
break
i2c.stop() # Stop I2C communication
def readfrom_mem(i2c, address, register, num_bytes):
"""
Read data from a specific register of an I2C device.
:param i2c: The SoftwareI2C object instance.
:param address: The 7-bit address of the I2C device.
:param register: The register address within the device from which to read.
:param num_bytes: The number of bytes to read from the register.
:return: A bytearray containing the data read from the device.
"""
# Start the I2C communication and send the device address in write mode
i2c.start()
if not i2c.write_byte(address << 1): # Shift address for write mode
print("Error: Device not acknowledging write mode.")
i2c.stop()
return
# Write the register address to read from
if not i2c.write_byte(register):
print("Error: Device not acknowledging register address.")
i2c.stop()
return
# Repeated start to switch to read mode
i2c.start()
if not i2c.write_byte((address << 1) | 1): # Shift address for read mode
print("Error: Device not acknowledging read mode.")
i2c.stop()
return
# Read the specified number of bytes
data = bytearray()
for i in range(num_bytes):
ack = i < num_bytes - 1 # ACK for all but the last byte
byte = i2c.read_byte(ack)
data.append(byte)
# Stop the I2C communication
i2c.stop()
return data