Acquiring Images from a USB mouse (Single Chip, AD

2020-07-22 09:16发布

问题:

I would like to extract the actual images captured by a single chip optical mouse sensor, specifically the ADNS-2700. As apposed to the various other tutorials on the internet that use a micro controller to talk to the SPI interface of the imaging chip (like this), the chip I am trying to work with has a USB interface integrated.

ADNS-2700 Datasheet

System: Windows 7, Python2.7, PyUSB 1.0

I have successfully extracted the button presses, velocity, and scroll wheel following this example:

import usb.core
import usb.util

VENDOR_ID = 6447
PRODUCT_ID = 2326

# find the USB device
device = usb.core.find(idVendor=VENDOR_ID,
                       idProduct=PRODUCT_ID)

# use the first/default configuration
device.set_configuration()

# first endpoint
endpoint = device[0][(0,0)][0]

# read a data packet
attempts = 10
data = None
while attempts > 0:
    try:
        data = device.read(endpoint.bEndpointAddress,
                           endpoint.wMaxPacketSize)
        print data

    except usb.core.USBError as e:
        data = None
        if e.args == ('Operation timed out',):
            attempts -= 1
            continue

Which extracts data like:

array('B', [0, 0, 16, 0, 0])
array('B', [0, 0, 240, 255, 0])
array('B', [0, 0, 16, 0, 0])
array('B', [0, 0, 240, 255, 0])

I need help extracting the raw image data!

I am a USB noob which is probably causing most of the problem.

On page 18 of the datasheet, there is a list of USB commands. The one that looks promising is:

Mnemonic            Command                    Notes
---------------------------------------------------------------
Get_Vendor_Test     C0 01 00 00 xx 00 01 00    Read register xx

Then on page 28, there is a list of registers of which this looks promising:

Address     Register Name    Register Type    Access       Reset Value
----------------------------------------------------------------------
0x0D        PIX_GRAB         Device           Read only    0x00

However, I have tried:

device.write(endpoint.bEndpointAddress,'C0:01:00:00:0A:00:01:00',0)

which results in a:

usb.core.USBError: [Errno None] libusb0-dll:err [_usb_setup_async] invalid endpoint 0x81

as well as:

device.read(endpoint.bEndpointAddress, 0x0D)

which simply times out.


Complete Solution:

import usb.core
import usb.util
import matplotlib.pyplot as plt
import numpy as np

VENDOR_ID = 6447
PRODUCT_ID = 2326

# find the USB device
device = usb.core.find(idVendor=VENDOR_ID,
                       idProduct=PRODUCT_ID)

# use the first/default configuration
device.set_configuration()


# In order to read the pixel bytes, reset PIX_GRAB by sending a write command
response = self.device.ctrl_transfer(bmRequestType = 0x40, #Write
                                     bRequest = 0x01,
                                     wValue = 0x0000,
                                     wIndex = 0x0D, #PIX_GRAB register value
                                     data_or_wLength = None
                                     )

# Read all the pixels (360 in this chip)
pixList = []
for i in range(361):
    response = self.device.ctrl_transfer(bmRequestType = 0xC0, #Read
                                         bRequest = 0x01,
                                         wValue = 0x0000,
                                         wIndex = 0x0D, #PIX_GRAB register value
                                         data_or_wLength = 1
                                         )
    pixList.append(response)

pixelArray = np.asarray(pixList)
pixelArray = pixelArray.reshape((19,19))

plt.imshow(pixelArray)
plt.show()

回答1:

You probably need to do ctrl_transfer() as shown in pyUSB tutorial.

You will also need to convert hex bytes from the datasheet into individual parameters to ctrl_transfer. See this page for the format.

Get_Vendor_Test C0 01 00 00 xx 00 01 00 can be issued with ctrl_transfer() call like this:

ret = dev.ctrl_transfer(bmRequestType=0xc0, # byte[0]
                        bRequest=0x01,      # byte[1]
                        wValue=0x0000,      # byte[2,3]
                        wIndex=register,    # byte[4,5]
                        data_or_wLength = 1)# byte[6,7]


回答2:

I just tried this code out myself, and there was a rather large bug in it. The datasheet for the ADNS-2700 states that response values that do not have the most-significant bit set are invalid. So the loop should not do exactly 361 reads, but should read until it gets 361 valid pixels, discarding any byte that does not have the "valid" bit set.

I made a couple other small changes, like unpacking the returned array before adding it to the list, and removing the MSb from the return values, since it doesn't represent actual pixel data.

I have tested this code using an Amazon Basics USB mouse, and it outputs a valid image.

import usb.core
import usb.util
import matplotlib.pyplot as plt
import numpy as np

VENDOR_ID = 0x04F2
PRODUCT_ID = 0x0939

# find the USB device
device = usb.core.find(idVendor=VENDOR_ID,
                       idProduct=PRODUCT_ID)

# use the first/default configuration
device.set_configuration()


# In order to read the pixel bytes, reset PIX_GRAB by sending a write command
response = device.ctrl_transfer(bmRequestType = 0x40, #Write
                                     bRequest = 0x01,
                                     wValue = 0x0000,
                                     wIndex = 0x0D, #PIX_GRAB register value
                                     data_or_wLength = None
                                     )

# Read all the pixels (360 in this chip)
pixList = []

while len(pixList) < 361:
    temp = 0
    response = device.ctrl_transfer(bmRequestType = 0xC0, #Read
                                         bRequest = 0x01,
                                         wValue = 0x0000,
                                         wIndex = 0x0D, #PIX_GRAB register value
                                         data_or_wLength = 1
                                         )
    if response[0] >= 0x80:
        temp = response[0] & 0x7F
        pixList.append(temp)

pixelArray = np.asarray(pixList)
pixelArray = pixelArray.reshape((19,19))

plt.imshow(pixelArray)
plt.show()