Artículos

Prueba tu HID con SL HID Tester

A lo largo de mis pruebas con PICs y su módulo USB estuve investigando cómo obtener velocidades útiles de transferencia de datos de hasta 50kB/s y gracias a varias recomendaciones llegué a la conclusión de que la clase HID en Full Speed sería mi mejor opción. También escribí un probador de dispositivos USB que me permitió conectarme con mi PIC más rápidamente que con otros programas para hacer estas pruebas.

El estándar USB 2.0 en Full Speed establece que los dispositivos de interfaz humana (HID) deben contar únicamente con 2 endpoints, uno de control y otro interruptivo. El endpoint de control se usará para configuración y enumeración del dispositivo y sus interfaces. El endpoint interruptivo se usará para envío y recepción de reportes.

 

El envío y recepción de datos se hace con una periodicidad constante en los endpoints interruptivos. Para Full Speed (12Mbps) este polling puede ser de 1ms a 255ms. Se entiende entonces que una interfaz podrá enviar datos cada 1ms a la pc y que la pc puede hacer lo mismo cada 1ms.

El tamaño de los reportes puede ser desde 1 byte hasta 64 bytes y considerando la velocidad del polling se tiene entonces un reporte de 64 bytes cada 1ms, entregando así una velocidad máxima de 64kB/s.

Al enviar paquetes a la PC cada 1ms y usar las librerías estándar USB me encontré con un problema... Windows no me recogía todos los paquetes... se perdían unos en el camino. Esto se debe a que las librerías solo leen un paquete por vez y Windows no le da prioridad a la lectura de datos porque hay mil y un procesos más que requieren usar el procesador.

Haciendo uso del libro USB Complete: The Developer's Guide (Fourth Edition) de Jan Axelson pude recibir los paquetes de mi PIC en modo HID sin tener pérdidas de información. Es necesario importar algunas funciones de las librerías internas de Windows XP: hid.dll, setupapi.dll y kernel32.dll.

Cuando Windows enumera un dispositivo HID y lo pasa al estado CONFIGURED es cuando el dispositivo comienza a enviar los reportes o paquetes cada que el host se lo indique, entendiendo host como hardware USB y no Windows.

Mi PIC quedó configurado a 1ms con reportes de 50bytes. Este periodo de 1ms es altamente exacto porque depende del host y no del scheduler de procesos de Winxp.

El kernel de windows se encarga de recibir los reportes que el pic envía cada 1ms y los almacena en un buffer circular (Ring Buffer). Este buffer tiene un tamaño predeterminado de 32 reportes en Windows XP y 2 reportes en Windows 98.

El usuario debe leer estos reportes antes de que el buffer circular se llene. Si llegan 33 reportes y no se lee el buffer entonces el reporte más viejo es sobreescrito y por lo tanto perdido.

Si cada reporte llega exactamente cada 1ms, entonces con 32 reportes es necesario que el usuario vacíe el buffer cada 32ms o menos. Mi problema era que las librerías que andan por internet solo leen un reporte por evento y para cuando se quiere leer otro, los viejos ya fueron sobreescritos.

Tuve que escribir mis propias importaciones de funciones para no perder paquetes. Las funciones importantes y que no vienen completas en el libro de Jan son:

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Boolean ReadFile(
            SafeFileHandle hFile,
            byte[] buffer, //IntPtr lpBuffer,
            Int32 nNumberOfBytesToRead,
            ref Int32 lpNumberOfBytesRead,
            IntPtr lpOverlapped);

        //Investiga tamaño del ring buffer de input reports
        [DllImport("hid.dll", SetLastError = true)]
        public static extern Boolean HidD_GetNumInputBuffers(
            SafeFileHandle HidDeviceObject,
            ref Int32 NumberBuffers);

        //Establece tamaño del ring buffer de input reports
        [DllImport("hid.dll", SetLastError = true)]
        public static extern Boolean HidD_SetNumInputBuffers(
            SafeFileHandle HidDeviceObject,
            Int32 NumberBuffers);


- GetNumInputBuffers
http://msdn.microsoft.com/en-us/library/ms790938.aspx
Sirve para leer el tamaño actual del buffer circular.

- SetNumInputBuffers
http://msdn.microsoft.com/en-us/library/ms790946.aspx
Sirve para cambiar el tamaño del buffer circular. Entre más grande el buffer circular menos frecuentemente habrá que hacer lecturas al mismo. Bajo Windows XP este buffer puede almacenar hasta 512 reportes.

- ReadFile
Después de obtener un handle al dispositivo USB se pueden leer de un solo paso todos los reportes que estén en el buffer circular, pidiendo nNumberOfBytesToRead como (reportes * bytes_por_reporte).

Al configurar el buffer circular con 512 reportes se tiene un almacenamiento de hasta 512ms de datos, obligando al usuario a solo vaciar el buffer cada 512ms o menos. Esto es bastante fácil de lograr con timers incluidos en Visual Studio sin importar cuanta carga tenga el CPU de Windows.

Un muestreo de 32ms pone mucha carga al procesador mientras que uno de 512ms es más ligero.

Gracias a estas funciones y que el kernel de windows nos guarda los paquetes temporalmente es posible leer datos a 50kB/s desde un PIC en modo HID sin pérdida de paquetes.

Velocidades de hasta 64kB/s por interfaz USB son posibles ya que el endpoint HID interruptivo puede enviar hasta 64bytes por polling.

A continuación dejo disponible el software de prueba para leer cualquier dispositivo HID enumerado bajo Windows.

Contraseña: MigSantiago.com

(Descarga)

Talvez sea necesario que instales el .NET Framework 3.5 para correrlo.
http://www.microsoft.com/downloads/details.aspx?displaylang=es&FamilyID=e3821449-3c6b-42f1-9fd9-0041345b3385

El uso del programa es simple...

+ Das click en Contar HIDs conectados. La consola te mostrará cuántos ha encontrado Windows.
+ Escribes el Vendor ID y Product ID de tu dispositivo HID y el número con el que salió en la consola (útil para cuando conectas más de un dispositivo idéntico a la misma PC).
+ Das click en Conectar dispositivo. Si la conexión es exitosa te lo indicará con VID y PID encontrados. Si la conexión falla es muy probable que estés queriendo conectarte a un teclado o mouse HID. Windows protege la lectura de estos últimos por cuestiones de seguridad.
+ Al conectarte exitosamente se incrementa o decrementa el tamaño del buffer circular del Kernel de Windows. Si en la consola la configuración del buffer muestra TRUE entonces el Kernel podrá almacenar todos los reportes que pediste. Por experiencia noté que en Windows XP solo se pueden almacenar hasta 512 reportes.
+ Posteriormente se debe dar click en Buscar capacidades. Esto entrega el tamaño de los reportes de entrada y salida del dispositivo más uno.
+ Al dar click en Leer un Reporte se obtendrá un único reporte del HID mostrándolo en pantalla. El primer byte indica el Report Type y los bytes siguientes son el contenido. En mi ejemplo el reporte mide 50 bytes pero al incluir el byte del tipo crece a 51. De la misma manera pasa con el Output Report.
+ Al dar click en Leer múltiples reportes se piden al buffer circular el máximo número de reportes que puede almacenar. Si el buffer estaba semi-lleno entregará menos reportes de los pedidos. Si el buffer ya se había saturado entregará todos los reportes pedidos, indicando así pérdida de paquetes (solo dada por el retraso entre clicks y no por falla del programa).

Este software fue escrito basándome en las muy útiles páginas de Jan Axelson, te recomiendo que visites el sitio por si quieres escribir tu propia librería HID. Igualmente ahí encontrarás el código fuente completo para generar tus aplicaciones.
http://www.lvr.com

El firmware del pic está basado en uno de los ejemplos del excelente compilador CCS. Puedes bajar un demo y analizarlo.
http://www.ccsinfo.com

SL HID Tester v0.31 puede contener bugs. El uso de este software es bajo única responsabilidad del usuario final.