1 """Chip Definition for Pico with u2if firmware"""
2 # https://github.com/execuc/u2if
8 """MCP2221 Device Class Definition"""
30 I2C0_WRITE_FROM_UART = 0x84
31 I2C1_INIT = I2C0_INIT + 0x10
32 I2C1_DEINIT = I2C0_DEINIT + 0x10
33 I2C1_WRITE = I2C0_WRITE + 0x10
34 I2C1_READ = I2C0_READ + 0x10
35 I2C1_WRITE_FROM_UART = I2C0_WRITE_FROM_UART + 0x10
42 SPI0_WRITE_FROM_UART = 0x64
43 SPI1_INIT = SPI0_INIT + 0x10
44 SPI1_DEINIT = SPI0_DEINIT + 0x10
45 SPI1_WRITE = SPI0_WRITE + 0x10
46 SPI1_READ = SPI0_READ + 0x10
47 SPI1_WRITE_FROM_UART = SPI0_WRITE_FROM_UART + 0x10
59 PWM_SET_DUTY_U16 = 0x34
60 PWM_GET_DUTY_U16 = 0x35
61 PWM_SET_DUTY_NS = 0x36
62 PWM_GET_DUTY_NS = 0x37
71 self._hid = hid.device()
72 self._hid.open(Pico_u2if.VID, Pico_u2if.PID)
73 self._i2c_index = None
74 self._spi_index = None
76 self._neopixel_initialized = False
77 self._uart_rx_buffer = None
79 def _hid_xfer(self, report, response=True):
80 """Perform HID Transfer"""
81 # first byte is report ID, which =0
82 # remaing bytes = 64 byte report data
83 # https://github.com/libusb/hidapi/blob/083223e77952e1ef57e6b77796536a3359c1b2a3/hidapi/hidapi.h#L185
84 self._hid.write(b"\0" + report + b"\0" * (64 - len(report)))
86 # return is 64 byte response report
87 return self._hid.read(64)
90 # ----------------------------------------------------------------
92 # ----------------------------------------------------------------
93 def gpio_init_pin(self, pin_id, direction, pull):
94 """Configure GPIO Pin."""
106 def gpio_set_pin(self, pin_id, value):
107 """Set Current GPIO Pin Value"""
118 def gpio_get_pin(self, pin_id):
119 """Get Current GPIO Pin Value"""
120 resp = self._hid_xfer(
129 return resp[3] != 0x00
131 # ----------------------------------------------------------------
133 # ----------------------------------------------------------------
134 def adc_init_pin(self, pin_id):
135 """Configure ADC Pin."""
145 def adc_get_value(self, pin_id):
146 """Get ADC value for pin."""
147 resp = self._hid_xfer(
156 return int.from_bytes(resp[3 : 3 + 2], byteorder="little")
158 # ----------------------------------------------------------------
160 # ----------------------------------------------------------------
161 def i2c_configure(self, baudrate, pullup=False):
163 if self._i2c_index is None:
164 raise RuntimeError("I2C bus not initialized.")
166 resp = self._hid_xfer(
169 self.I2C0_INIT if self._i2c_index == 0 else self.I2C1_INIT,
170 0x00 if not pullup else 0x01,
173 + baudrate.to_bytes(4, byteorder="little"),
176 if resp[1] != self.RESP_OK:
177 raise RuntimeError("I2C init error.")
179 def i2c_set_port(self, index):
181 if index not in (0, 1):
182 raise ValueError("I2C index must be 0 or 1.")
183 self._i2c_index = index
185 def _i2c_write(self, address, buffer, start=0, end=None, stop=True):
186 """Write data from the buffer to an address"""
187 if self._i2c_index is None:
188 raise RuntimeError("I2C bus not initialized.")
190 end = end if end else len(buffer)
192 write_cmd = self.I2C0_WRITE if self._i2c_index == 0 else self.I2C1_WRITE
193 stop_flag = 0x01 if stop else 0x00
195 while (end - start) > 0:
196 remain_bytes = end - start
197 chunk = min(remain_bytes, 64 - 7)
198 resp = self._hid_xfer(
199 bytes([write_cmd, address, stop_flag])
200 + remain_bytes.to_bytes(4, byteorder="little")
201 + buffer[start : (start + chunk)],
204 if resp[1] != self.RESP_OK:
205 raise RuntimeError("I2C write error")
208 def _i2c_read(self, address, buffer, start=0, end=None):
209 """Read data from an address and into the buffer"""
210 # TODO: support chunkified reads
211 if self._i2c_index is None:
212 raise RuntimeError("I2C bus not initialized.")
214 end = end if end else len(buffer)
216 read_cmd = self.I2C0_READ if self._i2c_index == 0 else self.I2C1_READ
217 stop_flag = 0x01 # always stop
218 read_size = end - start
220 resp = self._hid_xfer(bytes([read_cmd, address, stop_flag, read_size]), True)
221 if resp[1] != self.RESP_OK:
222 raise RuntimeError("I2C write error")
224 for i in range(read_size):
225 buffer[start + i] = resp[i + 2]
227 def i2c_writeto(self, address, buffer, *, start=0, end=None):
228 """Write data from the buffer to an address"""
229 self._i2c_write(address, buffer, start, end)
231 def i2c_readfrom_into(self, address, buffer, *, start=0, end=None):
232 """Read data from an address and into the buffer"""
233 self._i2c_read(address, buffer, start, end)
235 def i2c_writeto_then_readfrom(
246 """Write data from buffer_out to an address and then
247 read data from an address and into buffer_in
249 self._i2c_write(address, out_buffer, out_start, out_end, False)
250 self._i2c_read(address, in_buffer, in_start, in_end)
252 def i2c_scan(self, *, start=0, end=0x79):
253 """Perform an I2C Device Scan"""
254 if self._i2c_index is None:
255 raise RuntimeError("I2C bus not initialized.")
257 for addr in range(start, end + 1):
260 self.i2c_writeto(addr, b"\x00\x00\x00")
261 except RuntimeError: # no reply!
267 # ----------------------------------------------------------------
269 # ----------------------------------------------------------------
270 def spi_configure(self, baudrate):
272 if self._spi_index is None:
273 raise RuntimeError("SPI bus not initialized.")
275 resp = self._hid_xfer(
278 self.SPI0_INIT if self._spi_index == 0 else self.SPI1_INIT,
279 0x00, # mode, not yet implemented
282 + baudrate.to_bytes(4, byteorder="little"),
285 if resp[1] != self.RESP_OK:
286 raise RuntimeError("SPI init error.")
288 def spi_set_port(self, index):
290 if index not in (0, 1):
291 raise ValueError("SPI index must be 0 or 1.")
292 self._spi_index = index
294 def spi_write(self, buffer, *, start=0, end=None):
296 if self._spi_index is None:
297 raise RuntimeError("SPI bus not initialized.")
299 end = end if end else len(buffer)
301 write_cmd = self.SPI0_WRITE if self._spi_index == 0 else self.SPI1_WRITE
303 while (end - start) > 0:
304 remain_bytes = end - start
305 chunk = min(remain_bytes, 64 - 3)
306 resp = self._hid_xfer(
307 bytes([write_cmd, chunk]) + buffer[start : (start + chunk)], True
309 if resp[1] != self.RESP_OK:
310 raise RuntimeError("SPI write error")
313 def spi_readinto(self, buffer, *, start=0, end=None, write_value=0):
315 if self._spi_index is None:
316 raise RuntimeError("SPI bus not initialized.")
318 end = end if end else len(buffer)
319 read_cmd = self.SPI0_READ if self._spi_index == 0 else self.SPI1_READ
320 read_size = end - start
322 resp = self._hid_xfer(bytes([read_cmd, write_value, read_size]), True)
323 if resp[1] != self.RESP_OK:
324 raise RuntimeError("SPI write error")
326 for i in range(read_size):
327 buffer[start + i] = resp[i + 2]
329 def spi_write_readinto(
339 """SPI write and readinto."""
340 raise NotImplementedError("SPI write_readinto Not implemented")
342 # ----------------------------------------------------------------
344 # ----------------------------------------------------------------
345 def neopixel_write(self, gpio, buf):
346 """NeoPixel write."""
347 # open serial (data is sent over this)
348 if self._serial is None:
350 import serial.tools.list_ports
352 ports = serial.tools.list_ports.comports()
354 if port.vid == self.VID and port.pid == self.PID:
355 self._serial = serial.Serial(port.device)
357 if self._serial is None:
358 raise RuntimeError("Could not find Pico com port.")
361 if not self._neopixel_initialized:
362 # deinit any current setup
363 self._hid_xfer(bytes([self.WS2812B_DEINIT]))
364 resp = self._hid_xfer(
373 if resp[1] != self.RESP_OK:
374 raise RuntimeError("Neopixel init error")
375 self._neopixel_initialized = True
378 # command is done over HID
379 remain_bytes = len(buf)
380 resp = self._hid_xfer(
381 bytes([self.WS2812B_WRITE]) + remain_bytes.to_bytes(4, byteorder="little"),
384 if resp[1] != self.RESP_OK:
387 "Neopixel write error : too many pixel for the firmware."
389 elif resp[2] == 0x02:
391 "Neopixel write error : transfer already in progress."
394 raise RuntimeError("Neopixel write error")
395 # buffer is sent over serial
396 self._serial.write(buf)
399 # ----------------------------------------------------------------
401 # ----------------------------------------------------------------
402 def pwm_configure(self, pin, frequency=500, duty_cycle=0, variable_frequency=False):
405 resp = self._hid_xfer(bytes([self.PWM_INIT_PIN, pin.id]), True)
406 if resp[1] != self.RESP_OK:
407 raise RuntimeError("PWM init error.")
409 self.pwm_set_frequency(pin, frequency)
410 self.pwm_set_duty_cycle(pin, duty_cycle)
412 def pwm_deinit(self, pin):
414 self._hid_xfer(bytes([self.PWM_DEINIT_PIN, pin.id]))
416 def pwm_get_frequency(self, pin):
418 resp = self._hid_xfer(
419 bytes([self.PWM_GET_FREQ, pin.id])
420 + frequency.to_bytes(4, byteorder="little"),
423 if resp[1] != self.RESP_OK:
424 raise RuntimeError("PWM get frequency error.")
425 return int.from_bytes(resp[3 : 3 + 4], byteorder="little")
427 def pwm_set_frequency(self, pin, frequency):
429 resp = self._hid_xfer(
430 bytes([self.PWM_SET_FREQ, pin.id])
431 + frequency.to_bytes(4, byteorder="little"),
434 if resp[1] != self.RESP_OK:
436 raise RuntimeError("PWM different frequency on same slice.")
437 elif resp[3] == 0x02:
438 raise RuntimeError("PWM frequency too low.")
439 elif resp[3] == 0x03:
440 raise RuntimeError("PWM frequency too high.")
442 raise RuntimeError("PWM frequency error.")
444 def pwm_get_duty_cycle(self, pin):
445 """PWM get duty cycle."""
446 resp = self._hid_xfer(bytes([self.PWM_GET_DUTY_U16, pin.id]), True)
447 if resp[1] != self.RESP_OK:
448 raise RuntimeError("PWM get duty cycle error.")
449 return int.from_bytes(resp[3 : 3 + 4], byteorder="little")
451 def pwm_set_duty_cycle(self, pin, duty_cycle):
452 """PWM set duty cycle."""
453 resp = self._hid_xfer(
454 bytes([self.PWM_SET_DUTY_U16, pin.id])
455 + duty_cycle.to_bytes(2, byteorder="little"),
458 if resp[1] != self.RESP_OK:
459 raise RuntimeError("PWM set duty cycle error.")
462 pico_u2if = Pico_u2if()