|
45 | 45 | //|
|
46 | 46 | //| Example of emulating a simple device that can only handle single writes and reads::
|
47 | 47 | //|
|
48 |
| -//| |
49 | 48 | //| import board
|
50 | 49 | //| from i2ctarget import I2CTarget
|
51 | 50 | //|
|
|
56 | 55 | //| logger.setLevel(logging.INFO)
|
57 | 56 | //| logger.addHandler(NamedStreamHandler())
|
58 | 57 | //|
|
59 |
| -//| logger.info("\n\ncode starting...") |
| 58 | +//| logger.info("\\n\\ncode starting...") |
60 | 59 | //|
|
61 | 60 | //| # initialize an I2C target with a device address of 0x40
|
62 | 61 | //| with I2CTarget(board.SCL, board.SDA, (0x40,)) as device:
|
|
69 | 68 | //| # no request is pending
|
70 | 69 | //| continue
|
71 | 70 | //|
|
72 |
| -//| # `with` invokes I2CTargetRequest's functions to handle the necessary opening and closing of a transaction |
| 71 | +//| # `with` invokes I2CTargetRequest's functions to handle the necessary opening and closing of a request |
73 | 72 | //| with i2c_target_request:
|
74 | 73 | //|
|
75 | 74 | //| # the address associated with the request
|
|
87 | 86 | //| logger.info(f"write request to address 0x{address:02x}: {data}")
|
88 | 87 | //| # for our emulated device, writes have no effect
|
89 | 88 | //|
|
90 |
| -//| This example sets up an I2C device that can be accessed via another device running circuitpython:: |
91 |
| -//| import busio |
92 |
| -//| import board |
93 |
| -//| i2c = busio.I2C(board.SCL, board.SDA) |
94 |
| -//| |
95 |
| -//| |
96 |
| -//| # perform a single read |
97 |
| -//| while not i2c.try_lock(): |
98 |
| -//| pass |
99 |
| -//| buffer = bytearray(1) |
100 |
| -//| i2c.readfrom_into(0x40, buffer) |
101 |
| -//| print(f"device responded with {buffer}") |
102 |
| -//| i2c.unlock() |
103 |
| -//| |
104 |
| -//| # perform a single write |
105 |
| -//| while not i2c.try_lock(): |
106 |
| -//| pass |
107 |
| -//| buffer = bytearray(1) |
108 |
| -//| buffer[0] = 0x12 |
109 |
| -//| i2c.writeto(0x40, buffer) |
110 |
| -//| print(f"wrote {buffer} to device") |
111 |
| -//| i2c.unlock() |
112 |
| -//| |
113 |
| -//| Typically, i2c devices support writes and reads to/from multiple register indices:: |
114 |
| -//| import board |
115 |
| -//| from i2ctarget import I2CTarget |
116 |
| -//| |
117 |
| -//| import adafruit_logging as logging |
118 |
| -//| from logging import NamedStreamHandler |
119 |
| -//| |
120 |
| -//| logger = logging.getLogger('i2ctarget') |
121 |
| -//| logger.setLevel(logging.INFO) |
122 |
| -//| logger.addHandler(NamedStreamHandler()) |
123 |
| -//| |
124 |
| -//| # emulate a target with 16 registers |
125 |
| -//| regs = [0] * 16 |
126 |
| -//| register_index = None |
127 |
| -//| |
128 |
| -//| logger.info("\n\ncode starting...") |
129 |
| -//| |
130 |
| -//| # initialize an I2C target with a device address of 0x40 |
131 |
| -//| with I2CTarget(board.SCL, board.SDA, (0x40,)) as device: |
132 |
| -//| |
133 |
| -//| while True: |
134 |
| -//| # check if there's a pending device request |
135 |
| -//| i2c_target_request = device.request() |
136 |
| -//| |
137 |
| -//| if not i2c_target_request: |
138 |
| -//| # no request is pending |
139 |
| -//| continue |
140 |
| -//| |
141 |
| -//| # work with the i2c request |
142 |
| -//| with i2c_target_request: |
143 |
| -//| |
144 |
| -//| if not i2c_target_request.is_read: |
145 |
| -//| # a write request |
146 |
| -//| |
147 |
| -//| # bytearray contains the request's first byte, the register's index |
148 |
| -//| index = i2c_target_request.read(1)[0] |
149 |
| -//| |
150 |
| -//| # bytearray containing the request's second byte, the data |
151 |
| -//| data = i2c_target_request.read(1) |
152 |
| -//| |
153 |
| -//| # if the request doesn't have a second byte, this is read transaction |
154 |
| -//| if not data: |
155 |
| -//| |
156 |
| -//| # since we're only emulating 16 registers, read from a larger address is an error |
157 |
| -//| if index > 15: |
158 |
| -//| logger.error(f"write portion of read transaction has invalid index {index}") |
159 |
| -//| continue |
160 |
| -//| |
161 |
| -//| logger.info(f"write portion of read transaction, set index to {index}'") |
162 |
| -//| register_index = index |
163 |
| -//| continue |
164 |
| -//| |
165 |
| -//| # since we're only emulating 16 registers, writing to a larger address is an error |
166 |
| -//| if index > 15: |
167 |
| -//| logger.error(f"write request to incorrect index {index}") |
168 |
| -//| continue |
169 |
| -//| |
170 |
| -//| logger.info(f"write request to index {index}: {data}") |
171 |
| -//| regs[index] = data[0] |
172 |
| -//| else: |
173 |
| -//| # our emulated device requires a read to be part of a full write-then-read transaction |
174 |
| -//| if not i2c_target_request.is_restart: |
175 |
| -//| logger.warning(f"read request without first writing is not supported") |
176 |
| -//| # still need to respond, but result data is not defined |
177 |
| -//| i2c_target_request.write(bytes([0xff])) |
178 |
| -//| register_index = None |
179 |
| -//| continue |
180 |
| -//| |
181 |
| -//| # the single read transaction case is covered above, so we should always have a valid index |
182 |
| -//| assert(register_index is not None) |
183 |
| -//| |
184 |
| -//| # the write-then-read to an invalid address is covered above, |
185 |
| -//| # but if this is a restarted read, index might be out of bounds so need to check |
186 |
| -//| if register_index > 16: |
187 |
| -//| logger.error(f"restarted read yielded an unsupported index") |
188 |
| -//| i2c_target_request.write(bytes([0xff])) |
189 |
| -//| register_index = None |
190 |
| -//| continue |
191 |
| -//| |
192 |
| -//| # retrieve the data from our register file and respond |
193 |
| -//| data = regs[register_index] |
194 |
| -//| logger.info(f"read request from index {register_index}: {data}") |
195 |
| -//| i2c_target_request.write(bytes([data])) |
196 |
| -//| |
197 |
| -//| # in our emulated device, a single read transaction is covered above |
198 |
| -//| # so any subsequent restarted read gets the value at the next index |
199 |
| -//| assert(i2c_target_request.is_restart is True) |
200 |
| -//| register_index += 1 |
201 |
| -//| |
202 |
| -//| This second example creates I2C target device that can be accessed via another device running circuitpython:: |
203 |
| -//| import busio |
204 |
| -//| import board |
205 |
| -//| i2c = busio.I2C(board.SCL, board.SDA) |
206 |
| -//| |
207 |
| -//| # perform a write transaction |
208 |
| -//| while not i2c.try_lock(): |
209 |
| -//| pass |
210 |
| -//| buffer = bytearray(2) |
211 |
| -//| buffer[0] = 0x0b # the register index |
212 |
| -//| buffer[1] = 0xa1 # the value |
213 |
| -//| i2c.writeto(0x40, buffer) |
214 |
| -//| print(f"wrote {buffer} to device") |
215 |
| -//| i2c.unlock() |
216 |
| -//| |
217 |
| -//| # perform a full read transaction (write-then-read) |
218 |
| -//| while not i2c.try_lock(): |
219 |
| -//| pass |
220 |
| -//| index_buffer = bytearray(1) |
221 |
| -//| index_buffer[0] = 0x0b |
222 |
| -//| read_buffer = bytearray(1) |
223 |
| -//| i2c.writeto_then_readfrom(0x40, index_buffer, read_buffer) |
224 |
| -//| print(f"read from device index {index_buffer}: {read_buffer}") |
225 |
| -//| i2c.unlock() |
226 |
| -//| |
227 |
| -//| Or accessed from Linux like this:: |
| 89 | +//| This example creates an I2C target device that can be accessed via another device as an I2C controller:: |
| 90 | +//| |
| 91 | +//| import busio |
| 92 | +//| import board |
| 93 | +//| i2c = busio.I2C(board.SCL, board.SDA) |
| 94 | +//| |
| 95 | +//| # perform a single read |
| 96 | +//| while not i2c.try_lock(): |
| 97 | +//| pass |
| 98 | +//| buffer = bytearray(1) |
| 99 | +//| i2c.readfrom_into(0x40, buffer) |
| 100 | +//| print(f"device responded with {buffer}") |
| 101 | +//| i2c.unlock() |
| 102 | +//| |
| 103 | +//| # perform a single write |
| 104 | +//| while not i2c.try_lock(): |
| 105 | +//| pass |
| 106 | +//| buffer = bytearray(1) |
| 107 | +//| buffer[0] = 0x12 |
| 108 | +//| i2c.writeto(0x40, buffer) |
| 109 | +//| print(f"wrote {buffer} to device") |
| 110 | +//| i2c.unlock() |
| 111 | +//| |
| 112 | +//| Typically, i2c devices support writes and reads to/from multiple register indices as in this example :: |
| 113 | +//| |
| 114 | +//| import board |
| 115 | +//| from i2ctarget import I2CTarget |
| 116 | +//| |
| 117 | +//| import adafruit_logging as logging |
| 118 | +//| from logging import NamedStreamHandler |
| 119 | +//| |
| 120 | +//| logger = logging.getLogger('i2ctarget') |
| 121 | +//| logger.setLevel(logging.INFO) |
| 122 | +//| logger.addHandler(NamedStreamHandler()) |
| 123 | +//| |
| 124 | +//| # emulate a target with 16 registers |
| 125 | +//| regs = [0] * 16 |
| 126 | +//| register_index = None |
| 127 | +//| |
| 128 | +//| logger.info("\\n\\ncode starting...") |
| 129 | +//| |
| 130 | +//| # initialize an I2C target with a device address of 0x40 |
| 131 | +//| with I2CTarget(board.SCL, board.SDA, (0x40,)) as device: |
| 132 | +//| |
| 133 | +//| while True: |
| 134 | +//| # check if there's a pending device request |
| 135 | +//| i2c_target_request = device.request() |
| 136 | +//| |
| 137 | +//| if not i2c_target_request: |
| 138 | +//| # no request is pending |
| 139 | +//| continue |
| 140 | +//| |
| 141 | +//| # work with the i2c request |
| 142 | +//| with i2c_target_request: |
| 143 | +//| |
| 144 | +//| if not i2c_target_request.is_read: |
| 145 | +//| # a write request |
| 146 | +//| |
| 147 | +//| # bytearray contains the request's first byte, the register's index |
| 148 | +//| index = i2c_target_request.read(1)[0] |
| 149 | +//| |
| 150 | +//| # bytearray containing the request's second byte, the data |
| 151 | +//| data = i2c_target_request.read(1) |
| 152 | +//| |
| 153 | +//| # if the request doesn't have a second byte, this is read transaction |
| 154 | +//| if not data: |
| 155 | +//| |
| 156 | +//| # since we're only emulating 16 registers, read from a larger address is an error |
| 157 | +//| if index > 15: |
| 158 | +//| logger.error(f"write portion of read transaction has invalid index {index}") |
| 159 | +//| continue |
| 160 | +//| |
| 161 | +//| logger.info(f"write portion of read transaction, set index to {index}'") |
| 162 | +//| register_index = index |
| 163 | +//| continue |
| 164 | +//| |
| 165 | +//| # since we're only emulating 16 registers, writing to a larger address is an error |
| 166 | +//| if index > 15: |
| 167 | +//| logger.error(f"write request to incorrect index {index}") |
| 168 | +//| continue |
| 169 | +//| |
| 170 | +//| logger.info(f"write request to index {index}: {data}") |
| 171 | +//| regs[index] = data[0] |
| 172 | +//| else: |
| 173 | +//| # our emulated device requires a read to be part of a full write-then-read transaction |
| 174 | +//| if not i2c_target_request.is_restart: |
| 175 | +//| logger.warning(f"read request without first writing is not supported") |
| 176 | +//| # still need to respond, but result data is not defined |
| 177 | +//| i2c_target_request.write(bytes([0xff])) |
| 178 | +//| register_index = None |
| 179 | +//| continue |
| 180 | +//| |
| 181 | +//| # the single read transaction case is covered above, so we should always have a valid index |
| 182 | +//| assert(register_index is not None) |
| 183 | +//| |
| 184 | +//| # the write-then-read to an invalid address is covered above, |
| 185 | +//| # but if this is a restarted read, index might be out of bounds so need to check |
| 186 | +//| if register_index > 16: |
| 187 | +//| logger.error(f"restarted read yielded an unsupported index") |
| 188 | +//| i2c_target_request.write(bytes([0xff])) |
| 189 | +//| register_index = None |
| 190 | +//| continue |
| 191 | +//| |
| 192 | +//| # retrieve the data from our register file and respond |
| 193 | +//| data = regs[register_index] |
| 194 | +//| logger.info(f"read request from index {register_index}: {data}") |
| 195 | +//| i2c_target_request.write(bytes([data])) |
| 196 | +//| |
| 197 | +//| # in our emulated device, a single read transaction is covered above |
| 198 | +//| # so any subsequent restarted read gets the value at the next index |
| 199 | +//| assert(i2c_target_request.is_restart is True) |
| 200 | +//| register_index += 1 |
| 201 | +//| |
| 202 | +//| This second example creates I2C target device that can be accessed via another device as an I2C controller:: |
| 203 | +//| |
| 204 | +//| import busio |
| 205 | +//| import board |
| 206 | +//| i2c = busio.I2C(board.SCL, board.SDA) |
| 207 | +//| |
| 208 | +//| # perform a write transaction |
| 209 | +//| while not i2c.try_lock(): |
| 210 | +//| pass |
| 211 | +//| buffer = bytearray(2) |
| 212 | +//| buffer[0] = 0x0b # the register index |
| 213 | +//| buffer[1] = 0xa1 # the value |
| 214 | +//| i2c.writeto(0x40, buffer) |
| 215 | +//| print(f"wrote {buffer} to device") |
| 216 | +//| i2c.unlock() |
| 217 | +//| |
| 218 | +//| # perform a full read transaction (write-then-read) |
| 219 | +//| while not i2c.try_lock(): |
| 220 | +//| pass |
| 221 | +//| index_buffer = bytearray(1) |
| 222 | +//| index_buffer[0] = 0x0b |
| 223 | +//| read_buffer = bytearray(1) |
| 224 | +//| i2c.writeto_then_readfrom(0x40, index_buffer, read_buffer) |
| 225 | +//| print(f"read from device index {index_buffer}: {read_buffer}") |
| 226 | +//| i2c.unlock() |
| 227 | +//| |
| 228 | +//| Or accessed from Linux like this:: |
228 | 229 | //|
|
229 | 230 | //| $ i2cget -y 1 0x40 0x0b
|
230 | 231 | //| 0xff
|
|
0 commit comments