diff --git a/hw/production_test/production_test.py b/hw/production_test/production_test.py index d6f540c..df4f556 100755 --- a/hw/production_test/production_test.py +++ b/hw/production_test/production_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -import hid_test +import usb_test import time import numpy from subprocess import run @@ -24,7 +24,7 @@ file_locations = { def enable_power(): """Enable power to the TK-1""" - d = hid_test.ice40_flasher() + d = usb_test.ice40_flasher() d.gpio_set_direction(7, True) d.gpio_put(7, True) d.close() @@ -35,7 +35,7 @@ def enable_power(): def disable_power(): """Disable power to the TK-1""" time.sleep(.1) - d = hid_test.ice40_flasher() + d = usb_test.ice40_flasher() d.gpio_set_direction(7, True) d.gpio_put(7, False) d.close() @@ -54,7 +54,7 @@ def voltage_test(): """Measure 3.3V 2.5V, and 1.2V voltage rails on the TK-1""" enable_power() - d = hid_test.ice40_flasher() + d = usb_test.ice40_flasher() vals = measure_voltages(d,20) d.close() @@ -127,7 +127,7 @@ def test_extra_io(): enable_power() time.sleep(.1) - d = hid_test.ice40_flasher() + d = usb_test.ice40_flasher() d.gpio_put(16, False) d.gpio_set_direction(16, True) diff --git a/hw/production_test/usb_test.py b/hw/production_test/usb_test.py new file mode 100755 index 0000000..c025c69 --- /dev/null +++ b/hw/production_test/usb_test.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +import usb.core +import usb.util +import struct + +class ice40_flasher: + FLASHER_REQUEST_LED_SET = 0x00, + FLASHER_REQUEST_PIN_DIRECTION_SET = 0x10 + FLASHER_REQUEST_PULLUPS_SET = 0x12 + FLASHER_REQUEST_PIN_VALUES_SET = 0x20 + FLASHER_REQUEST_PIN_VALUES_GET = 0x30 + FLASHER_REQUEST_SPI_BITBANG = 0x40 + FLASHER_REQUEST_ADC_READ = 0x50 + + def __init__(self): +# self.dev = None +# for dict in hid.enumerate(USB_VID): +# self.dev = hid.Device(dict['vendor_id'], dict['product_id']) +# if self.dev is None: +# raise IOError("Couldn't find any hid device with vendor id 0x%x" % (USB_VID)) + + # See: https://github.com/pyusb/pyusb/blob/master/docs/tutorial.rst + self.dev = usb.core.find(idVendor=0xcafe, idProduct=0x4010) + + if self.dev is None: + raise ValueError('Device not found') + + self.dev.set_configuration() + + def close(self): + """Close the HID device""" + #self.dev.close() + pass + + def _write(self, request_id, data): + self.dev.ctrl_transfer(0x40, request_id,0,0,data) + + def _read(self, request_id, length): + #ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength = None, timeout = None): + # Request type: + # bit 7: direction 0:host to device (OUT), 1: device to host (IN) + # bits 5-6: type: 0:standard 1:class 2:vendor 3:reserved + # bits 0-4: recipient: 0:device 1:interface 2:endpoint 3:other + ret = self.dev.ctrl_transfer(0xC0, request_id,0,0,length) + return ret + +# def led_set(self, value: bool) -> None: +# """Set the state of the onboard LED + +# Keyword arguments: +# value -- True: On, False: Off +# """ +# msg = struct.pack('>BBB', +# 0x0, +# 0x00, +# (1 if value else 0) +# ) + +# #print(['{:02x}'.format(b) for b in msg]) +# self.dev.write(msg) + + def gpio_set_direction(self, pin: int, direction: bool) -> None: + """Set the direction of a single GPIO pin + + Keyword arguments: + pin -- GPIO pin number + value -- True: Set pin as output, False: set pin as input + """ + msg = struct.pack('>II', + (1< None: + """ True: Enable pullup/pulldown, False: Disable pullup/pulldown """ + """Configure the pullup and pulldown resistors for a single GPIO pin + + Keyword arguments: + pin -- GPIO pin number + pullup -- True: Enable pullup, False: Disable pullup + pulldown -- True: Enable pulldown, False: Disable pulldown + """ + msg = struct.pack('>III', + (1< None: + """Set the output level of a single GPIO pin + + Keyword arguments: + pin -- GPIO pin number + val -- True: High, False: Low + """ + msg = struct.pack('>II', + 1 << pin, + (1 if val else 0) << pin, + ) + + self._write(self.FLASHER_REQUEST_PIN_VALUES_SET, msg) + + def gpio_get_all(self) -> int: + """Read the input levels of all GPIO pins""" + msg_in = self._read(self.FLASHER_REQUEST_PIN_VALUES_GET,4) + [gpio_states] = struct.unpack('>I', msg_in) + + return gpio_states + + def gpio_get(self, pin: int) -> bool: + """Read the input level of a single GPIO pin + + Keyword arguments: + pin -- GPIO pin number + """ + gpio_states = gpio_get_all() + + return ((gpio_states >> pin) & 0x01) == 0x01 + + def spi_bitbang( + self, + sck_pin: int, + mosi_pin: int, + miso_pin: int, + buf: bytearray) -> bytearray: + """Bitbang a SPI transfer using the specificed GPIO pins + + Note that this command does not handle setting a CS pin, that must be accomplished + separately, for instance by calling gpio_set() on the pin controlling the CS line. + + Keyword arguments: + sck_pin -- GPIO pin number to use as the SCK signal + mosi_pin -- GPIO pin number to use as the MOSI signal + miso_pin -- GPIO pin number to use as the MISO signal + buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent. + bit_count -- (Optional) Number of bits (not bytes) to bitbang. If left unspecificed, defaults to the size of buf. + """ + + ret = bytearray() + + max_chunk_size = (1024-8) + for i in range(0, len(buf), max_chunk_size): + chunk = buf[i:i + max_chunk_size] + ret.extend( + self.spi_bitbang_inner( + sck_pin=sck_pin, + mosi_pin=mosi_pin, + miso_pin=miso_pin, + buf=chunk)) + + return ret + + def spi_bitbang_inner( + self, + sck_pin: int, + mosi_pin: int, + miso_pin: int, + buf: bytearray, + bit_count: int = -1) -> bytearray: + """Bitbang a SPI transfer using the specificed GPIO pins + + Note that this command does not handle setting a CS pin, that must be accomplished + separately, for instance by calling gpio_set() on the pin controlling the CS line. + + Keyword arguments: + sck_pin -- GPIO pin number to use as the SCK signal + mosi_pin -- GPIO pin number to use as the MOSI signal + miso_pin -- GPIO pin number to use as the MISO signal + buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent. + bit_count -- (Optional) Number of bits (not bytes) to bitbang. If left unspecificed, defaults to the size of buf. + """ + if bit_count == -1: + bit_count = len(buf) * 8 + + byte_length = (bit_count+7)//8 + + if byte_length > (1024-8): + print('Message too large, bit_count:{:}'.format(bit_count)) + exit(1) + + if byte_length != len(buf): + print( + 'Bit count size mismatch, bit_count:{:} len(buf):{:}'.format(bit_count), + len(buf) * 8) + exit(1) + + header = struct.pack('>BBBI', + sck_pin, + mosi_pin, + miso_pin, + bit_count) + msg = bytearray() + msg.extend(header) + msg.extend(buf) + + self._write(self.FLASHER_REQUEST_SPI_BITBANG,msg) + + msg_in = self._read(self.FLASHER_REQUEST_SPI_BITBANG, byte_length) + + return msg_in + + def adc_read_all(self) -> list[float]: + """Read the voltage values of ADC 0, 1, and 2 + + The firmware will read the values for each input multiple times, and return averaged values for each input. + """ + msg_in = self._read(self.FLASHER_REQUEST_ADC_READ,3*4) + [ch0, ch1, ch2] = struct.unpack('>III', msg_in) + + return ch0/1000000, ch1/1000000, ch2/1000000 + +if __name__ == '__main__': + flasher = ice40_flasher() + print(flasher.gpio_get_all()) + print(flasher.adc_read_all()) + + # for pin in range(10,13): + # flasher.gpio_set_direction(pin, True) + + # while True: + # for pin in range(10,13): + # flasher.gpio_put(pin, True) + # flasher.gpio_put(pin, False) + + flasher.gpio_set_direction(10, True) + flasher.gpio_set_direction(11, True) + flasher.gpio_set_direction(13, False) + + + buf = [0x01,0x02,0x03, 0xFE] + header = struct.pack('>BBBI', + 10, + 11, + 13, + len(buf)*8) + msg = bytearray() + msg.extend(header) + msg.extend(buf) + + while True: + + flasher.spi_bitbang_inner(sck_pin=10, mosi_pin=11, miso_pin=13, buf=buf)