1 """Chip Definition for Pico with u2if firmware"""
2 # https://github.com/execuc/u2if
7 # pylint: disable=import-outside-toplevel,too-many-branches,too-many-statements
8 # pylint: disable=too-many-arguments,too-many-function-args, too-many-public-methods
12 """MCP2221 Device Class Definition"""
35 I2C0_WRITE_FROM_UART = 0x84
36 I2C1_INIT = I2C0_INIT + 0x10
37 I2C1_DEINIT = I2C0_DEINIT + 0x10
38 I2C1_WRITE = I2C0_WRITE + 0x10
39 I2C1_READ = I2C0_READ + 0x10
40 I2C1_WRITE_FROM_UART = I2C0_WRITE_FROM_UART + 0x10
47 SPI0_WRITE_FROM_UART = 0x64
48 SPI1_INIT = SPI0_INIT + 0x10
49 SPI1_DEINIT = SPI0_DEINIT + 0x10
50 SPI1_WRITE = SPI0_WRITE + 0x10
51 SPI1_READ = SPI0_READ + 0x10
52 SPI1_WRITE_FROM_UART = SPI0_WRITE_FROM_UART + 0x10
64 PWM_SET_DUTY_U16 = 0x34
65 PWM_GET_DUTY_U16 = 0x35
66 PWM_SET_DUTY_NS = 0x36
67 PWM_GET_DUTY_NS = 0x37
70 self._i2c_index = None
71 self._spi_index = None
73 self._neopixel_initialized = False
74 self._uart_rx_buffer = None
77 def _hid_xfer(self, report, response=True):
78 """Perform HID Transfer"""
79 # first byte is report ID, which =0
80 # remaing bytes = 64 byte report data
81 # https://github.com/libusb/hidapi/blob/083223e77952e1ef57e6b77796536a3359c1b2a3/hidapi/hidapi.h#L185
82 self._hid.write(b"\0" + report + b"\0" * (64 - len(report)))
84 # return is 64 byte response report
85 return self._hid.read(64)
90 self._hid = hid.device()
92 self._hid.open(Pico_u2if.VID, Pico_u2if.PID)
93 resp = self._hid_xfer(bytes([self.SYS_RESET]), True)
94 if resp[1] != self.RESP_OK:
95 raise RuntimeError("Reset error.")
101 self._hid.open(Pico_u2if.VID, Pico_u2if.PID)
104 if retries >= max_retry:
110 # ----------------------------------------------------------------
112 # ----------------------------------------------------------------
113 def gpio_init_pin(self, pin_id, direction, pull):
114 """Configure GPIO Pin."""
126 def gpio_set_pin(self, pin_id, value):
127 """Set Current GPIO Pin Value"""
138 def gpio_get_pin(self, pin_id):
139 """Get Current GPIO Pin Value"""
140 resp = self._hid_xfer(
149 return resp[3] != 0x00
151 # ----------------------------------------------------------------
153 # ----------------------------------------------------------------
154 def adc_init_pin(self, pin_id):
155 """Configure ADC Pin."""
165 def adc_get_value(self, pin_id):
166 """Get ADC value for pin."""
167 resp = self._hid_xfer(
176 return int.from_bytes(resp[3 : 3 + 2], byteorder="little")
178 # ----------------------------------------------------------------
180 # ----------------------------------------------------------------
181 def i2c_configure(self, baudrate, pullup=False):
183 if self._i2c_index is None:
184 raise RuntimeError("I2C bus not initialized.")
186 resp = self._hid_xfer(
189 self.I2C0_INIT if self._i2c_index == 0 else self.I2C1_INIT,
190 0x00 if not pullup else 0x01,
193 + baudrate.to_bytes(4, byteorder="little"),
196 if resp[1] != self.RESP_OK:
197 raise RuntimeError("I2C init error.")
199 def i2c_set_port(self, index):
201 if index not in (0, 1):
202 raise ValueError("I2C index must be 0 or 1.")
203 self._i2c_index = index
205 def _i2c_write(self, address, buffer, start=0, end=None, stop=True):
206 """Write data from the buffer to an address"""
207 if self._i2c_index is None:
208 raise RuntimeError("I2C bus not initialized.")
210 end = end if end else len(buffer)
212 write_cmd = self.I2C0_WRITE if self._i2c_index == 0 else self.I2C1_WRITE
213 stop_flag = 0x01 if stop else 0x00
215 while (end - start) > 0:
216 remain_bytes = end - start
217 chunk = min(remain_bytes, 64 - 7)
218 resp = self._hid_xfer(
219 bytes([write_cmd, address, stop_flag])
220 + remain_bytes.to_bytes(4, byteorder="little")
221 + buffer[start : (start + chunk)],
224 if resp[1] != self.RESP_OK:
225 raise RuntimeError("I2C write error")
228 def _i2c_read(self, address, buffer, start=0, end=None):
229 """Read data from an address and into the buffer"""
230 # TODO: support chunkified reads
231 if self._i2c_index is None:
232 raise RuntimeError("I2C bus not initialized.")
234 end = end if end else len(buffer)
236 read_cmd = self.I2C0_READ if self._i2c_index == 0 else self.I2C1_READ
237 stop_flag = 0x01 # always stop
238 read_size = end - start
240 resp = self._hid_xfer(bytes([read_cmd, address, stop_flag, read_size]), True)
241 if resp[1] != self.RESP_OK:
242 raise RuntimeError("I2C write error")
244 for i in range(read_size):
245 buffer[start + i] = resp[i + 2]
247 def i2c_writeto(self, address, buffer, *, start=0, end=None):
248 """Write data from the buffer to an address"""
249 self._i2c_write(address, buffer, start, end)
251 def i2c_readfrom_into(self, address, buffer, *, start=0, end=None):
252 """Read data from an address and into the buffer"""
253 self._i2c_read(address, buffer, start, end)
255 def i2c_writeto_then_readfrom(
266 """Write data from buffer_out to an address and then
267 read data from an address and into buffer_in
269 self._i2c_write(address, out_buffer, out_start, out_end, False)
270 self._i2c_read(address, in_buffer, in_start, in_end)
272 def i2c_scan(self, *, start=0, end=0x79):
273 """Perform an I2C Device Scan"""
274 if self._i2c_index is None:
275 raise RuntimeError("I2C bus not initialized.")
277 for addr in range(start, end + 1):
280 self.i2c_writeto(addr, b"\x00\x00\x00")
281 except RuntimeError: # no reply!
287 # ----------------------------------------------------------------
289 # ----------------------------------------------------------------
290 def spi_configure(self, baudrate):
292 if self._spi_index is None:
293 raise RuntimeError("SPI bus not initialized.")
295 resp = self._hid_xfer(
298 self.SPI0_INIT if self._spi_index == 0 else self.SPI1_INIT,
299 0x00, # mode, not yet implemented
302 + baudrate.to_bytes(4, byteorder="little"),
305 if resp[1] != self.RESP_OK:
306 raise RuntimeError("SPI init error.")
308 def spi_set_port(self, index):
310 if index not in (0, 1):
311 raise ValueError("SPI index must be 0 or 1.")
312 self._spi_index = index
314 def spi_write(self, buffer, *, start=0, end=None):
316 if self._spi_index is None:
317 raise RuntimeError("SPI bus not initialized.")
319 end = end if end else len(buffer)
321 write_cmd = self.SPI0_WRITE if self._spi_index == 0 else self.SPI1_WRITE
323 while (end - start) > 0:
324 remain_bytes = end - start
325 chunk = min(remain_bytes, 64 - 3)
326 resp = self._hid_xfer(
327 bytes([write_cmd, chunk]) + buffer[start : (start + chunk)], True
329 if resp[1] != self.RESP_OK:
330 raise RuntimeError("SPI write error")
333 def spi_readinto(self, buffer, *, start=0, end=None, write_value=0):
335 if self._spi_index is None:
336 raise RuntimeError("SPI bus not initialized.")
338 end = end if end else len(buffer)
339 read_cmd = self.SPI0_READ if self._spi_index == 0 else self.SPI1_READ
340 read_size = end - start
342 resp = self._hid_xfer(bytes([read_cmd, write_value, read_size]), True)
343 if resp[1] != self.RESP_OK:
344 raise RuntimeError("SPI write error")
346 for i in range(read_size):
347 buffer[start + i] = resp[i + 2]
349 def spi_write_readinto(
359 """SPI write and readinto."""
360 raise NotImplementedError("SPI write_readinto Not implemented")
362 # ----------------------------------------------------------------
364 # ----------------------------------------------------------------
365 def neopixel_write(self, gpio, buf):
366 """NeoPixel write."""
367 # open serial (data is sent over this)
368 if self._serial is None:
370 import serial.tools.list_ports
372 ports = serial.tools.list_ports.comports()
374 if port.vid == self.VID and port.pid == self.PID:
375 self._serial = serial.Serial(port.device)
377 if self._serial is None:
378 raise RuntimeError("Could not find Pico com port.")
381 if not self._neopixel_initialized:
382 # deinit any current setup
383 # pylint: disable=protected-access
384 self._hid_xfer(bytes([self.WS2812B_DEINIT]))
385 resp = self._hid_xfer(
394 if resp[1] != self.RESP_OK:
395 raise RuntimeError("Neopixel init error")
396 self._neopixel_initialized = True
399 # command is done over HID
400 remain_bytes = len(buf)
401 resp = self._hid_xfer(
402 bytes([self.WS2812B_WRITE]) + remain_bytes.to_bytes(4, byteorder="little"),
405 if resp[1] != self.RESP_OK:
406 # pylint: disable=no-else-raise
409 "Neopixel write error : too many pixel for the firmware."
411 elif resp[2] == 0x02:
413 "Neopixel write error : transfer already in progress."
416 raise RuntimeError("Neopixel write error")
417 # buffer is sent over serial
418 self._serial.write(buf)
421 # ----------------------------------------------------------------
423 # ----------------------------------------------------------------
424 # pylint: disable=unused-argument
425 def pwm_configure(self, pin, frequency=500, duty_cycle=0, variable_frequency=False):
428 resp = self._hid_xfer(bytes([self.PWM_INIT_PIN, pin.id]), True)
429 if resp[1] != self.RESP_OK:
430 raise RuntimeError("PWM init error.")
432 self.pwm_set_frequency(pin, frequency)
433 self.pwm_set_duty_cycle(pin, duty_cycle)
435 def pwm_deinit(self, pin):
437 self._hid_xfer(bytes([self.PWM_DEINIT_PIN, pin.id]))
439 def pwm_get_frequency(self, pin):
441 resp = self._hid_xfer(bytes([self.PWM_GET_FREQ, pin.id]), True)
442 if resp[1] != self.RESP_OK:
443 raise RuntimeError("PWM get frequency error.")
444 return int.from_bytes(resp[3 : 3 + 4], byteorder="little")
446 def pwm_set_frequency(self, pin, frequency):
448 resp = self._hid_xfer(
449 bytes([self.PWM_SET_FREQ, pin.id])
450 + frequency.to_bytes(4, byteorder="little"),
453 if resp[1] != self.RESP_OK:
454 # pylint: disable=no-else-raise
456 raise RuntimeError("PWM different frequency on same slice.")
457 elif resp[3] == 0x02:
458 raise RuntimeError("PWM frequency too low.")
459 elif resp[3] == 0x03:
460 raise RuntimeError("PWM frequency too high.")
462 raise RuntimeError("PWM frequency error.")
464 def pwm_get_duty_cycle(self, pin):
465 """PWM get duty cycle."""
466 resp = self._hid_xfer(bytes([self.PWM_GET_DUTY_U16, pin.id]), True)
467 if resp[1] != self.RESP_OK:
468 raise RuntimeError("PWM get duty cycle error.")
469 return int.from_bytes(resp[3 : 3 + 4], byteorder="little")
471 def pwm_set_duty_cycle(self, pin, duty_cycle):
472 """PWM set duty cycle."""
473 resp = self._hid_xfer(
474 bytes([self.PWM_SET_DUTY_U16, pin.id])
475 + duty_cycle.to_bytes(2, byteorder="little"),
478 if resp[1] != self.RESP_OK:
479 raise RuntimeError("PWM set duty cycle error.")
482 pico_u2if = Pico_u2if()