|
EL
USB
DESENCADENADO : HID USB
|
|
|
Clase de dispositivos de
comunicaciones |
|
|
Cómo realizar un proyecto
con el USB 2.0 emulando un Dispositivo de Interfaz Humana, o sea un
HID USB.
Un PIC con USB 2.0 que funciona como un Teclado USB, aunque podríamos
implementar también un Ratón o incluso un Joystick.
Nota: Si no queréis leer nada sino que por el contrario aplicáis aquel
principio que dice "Los read.me son para cobardes ¡Ejecuta!"
podéis ir directamente a Recursos, todo lo
necesario. |
|
|
PIC's, Hardware mínimo y Antecedentes
varios. |
|
|
|
|
|
|
|
|
Descripción del
proyecto. |
|
|
|
-
Para el firmware
vamos a utilizar como base el ejemplo y descriptor USB que trae como
ejemplos el CCS C, adaptándolo a nuestras necesidades, fundamentalmente
darles los VID&PID que espera encontrar el driver para
Windows
|
|
|
Firmware. |
|
|
-
<> Firmware para el PIC 18Fxx5x
realizado en CCS C
|
- Este firmware está compilado con el
compilador PCH CCS C versión 3.242.
- Las palabras clave y los puntos de
interés de este firmware estan marcados en
negrita.
- El descriptor USB está incluido en el
fichero USB_Kbd_HID.h que aparece marcado en
rojo.
(Se incluye mas abajo)
|
#fuses HSPLL y PLL5
|
La frecuencia de oscilación necesaria
para el USB 2.0 es de 48 Mhz. Como estamos utilizando en nuestro
hardware un cristal de cuarzo de 20 Mhz necesitamos hacer uso del módulo
PLL interno del PIC. Para ello utilizamos el fuse HSPLL.
Como el módulo PLL requiere una oscilación de entrada de 4 Mhz
debemos utilizar el divisor 1:5 indicado con el fuse PLL5 para
obtener los 20:5 = 4 Mhz requeridos. (Para mas detalles consultar Consiguiendo 4 Mhz para los 48
Mhz necesarios en los PIC's con USB 2.0 en esta misma página)
|
#include USB_kbd_HID.h
|
En el fichero usb_kbd_HID.h,
cargado con el correspondiente include, se definen las
estructuras y parámetros necesarios para la conexión USB. Se discutirá
su contenido un poco mas abajo donde además se muestra su contenido
completo.
|
Los código a enviar como Keys pulsadas
|
Consultar el documento de Microsoft:
USB HID .pdfS2 Scan Code Translation Table
|
|
|
rr2_hid_bkd_usb_monitor.c
|
|
|
/////////////////////////////////////////////////////////////////////////
////
//// rr2_hid_kbd_usb_monitor.c
////
//// by RedPic
////
//// 08/10/2006
////
/////////////////////////////////////////////////////////////////////////
#include <18F4550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)
#DEFINE USB_HID_DEVICE TRUE
#define USB_EP1_TX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for IN bulk/interrupt
transfers
#define USB_EP1_TX_SIZE 8
#define USB_EP1_RX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for IN bulk/interrupt
transfers
#define USB_EP1_RX_SIZE 8
#include <pic18_usb.h>
#include <.\include\usb_kbd_HID.h> //USB Configuration and Device
descriptors for this UBS device
#include <usb.c> //handles usb setup tokens and get descriptor reports
#include <ctype.h>
/////////////////////////////////////////////////////////////////////////////
//
// Defines y Zarandajas
//
/////////////////////////////////////////////////////////////////////////////
#define LED1 PIN_E0
#define LED2 PIN_E1
#define LED3 PIN_E2
#define LED_ON output_high
#define LED_OFF output_low
#define PIN_SOURCE PIN_B1
/////////////////////////////////////////////////////////////////////////////
//
// RAM
//
/////////////////////////////////////////////////////////////////////////////
int8 connected;
int8 enumerated;
int8 rx_msg[USB_EP1_RX_SIZE];
int8 tx_msg[8]={2,0,0,0,0,0,0,0};
char NextChar='0';
int1 hay_dato=0;
int8 i;
/////////////////////////////////////////////////////////////////////////////
//
// usb_debug_task()
//
// When called periodically, displays debugging information over serial
// to display enumeration and connection states. Also lights LED2 and LED3
// based upon enumeration and connection status.
//
/////////////////////////////////////////////////////////////////////////////
void usb_debug_task(void) {
enumerated=usb_enumerated();
if(enumerated){
LED_ON(LED1);
LED_OFF(LED3);
}
else{
LED_ON(LED3);
LED_OFF(LED1);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// usb_keyboard_task()
//
// Sends a packet of keyboard data. The protocol was specified in the HID
// report descriptor (see usb_desc_kbmouse.h), and is:
// tx_msg[0] = HID report id (2)
// tx_msg[1] = modifier (an 8bit bitmap of shift, tab, alt keypress)
// tx_msg[2] = const 0
// tx_msg[3:7] = an array of held down keys. a=4, b=5, etc.
// if msg[2:7]={0} then no keys are held down
//
// rx_msg[1] = HID report id (2)
// rx_msg[0] = 5bit bitmap of led status
//
/////////////////////////////////////////////////////////////////////////////
int8 char_2_usb_kbd_code(char c){
int8 ic;
if(isAlpha(c)){
ic=c-'a'+4;
}
else{
if(c=='0'){
ic=39;
}
else{
ic=c-'1'+30;
}
}
return(ic);
}
void usb_keyboard_task(void) {
static char Char_Tx;
if(hay_dato==1){
hay_dato=0;
tx_msg[3]=char_2_usb_kbd_code(NextChar);
usb_put_packet(1,tx_msg,sizeof(tx_msg),USB_DTS_TOGGLE);
++NextChar;
if(NextChar>'z'){
NextChar='0';
}
if(NextChar>'9'&&NextChar<'a'){
NextChar='a';
}
LED_OFF(LED2);
delay_ms(5);
}else{
tx_msg[3]=0;
usb_put_packet(1,tx_msg,sizeof(tx_msg),USB_DTS_TOGGLE);
delay_ms(5);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// usb_rx_task()
//
// Listens to EP1 for any incoming packets. The only report ID that is
// configurd to send us data is 2 (keyboard LED status, see above)
//
/////////////////////////////////////////////////////////////////////////////
void usb_rx_task(void){
if (usb_kbhit(1)){
usb_get_packet(1, rx_msg, sizeof(rx_msg));
}
}
/////////////////////////////////////////////////////////////////////////////
//
// recibe por interrupción ext
//
/////////////////////////////////////////////////////////////////////////////
#int_ext
void ext_handler(void){
if(hay_dato==0){
hay_dato=1;
LED_ON(LED2);
}
}
void main() {
hay_dato=0;
delay_ms(500);
LED_OFF(LED1);
LED_OFF(LED2);
LED_OFF(LED3);
usb_init_cs();
port_b_pullups(true);
set_tris_b(0b00000001);
output_high(PIN_SOURCE);
ext_int_edge(0,L_TO_H);
enable_interrupts(int_ext);
enable_interrupts(global);
while (TRUE) {
usb_task();
usb_debug_task();
if (usb_enumerated()) {
usb_keyboard_task();
usb_rx_task();
delay_ms(5);
}
}
}
|
|
|
|
|
|
- El el fichero USB_Kbd_HID.h
vamos a incluir las estructuras y parámetros necesarios para una
correcta conexión con el driver del PC.
|
- Es un tema absolutamente complejo e
inacabable, muy propio de la gente de Microsoft, que debe adaptarse a la
estructura que espera recibir el driver del Windows XP. Nosotros vamos a
centrarnos únicamente en un par de puntos que generan la mayoría de las
consultas que recibo: El concepto del VID&PID y la modificación
de la tabla USB_STRING_DESC[].
|
VID&PID
|
El VID es un número de 16 bits
que significa Vendor Identification o código que identifica al
fabricante del hardware a conectar. En nuestro caso utilizamos el
número 04D8h que identifica a Microchip.
|
El PID es un número de 16 bits
que significa Product Identification o código que identifica al
dispositivo en concreto hardware a conectar. En nuestro caso
utilizamos el número 000Ah que identifica a la familia de los
PIC18 de este fabricante.
|
Tened en cuenta que
la conjunción de estos dos numeros VID&PID es la que nos va a servir
para conectar con el Driver de Windows XP
Cuando el S.O. conecte con nuestro
firmware recibirá el VID&PID y buscará entre sus drivers
instalados para encontrar el que corresponde a esta identificación, si
no la encuentra nos preguntará sobre donde ha de buscar un driver
adecuado y deberemos indicarle su ubicación. Este driver deberá estar
configurado para conectar con un hardware cuyo VID&PID sea el
mismo. No olvidad que el driver para puertos serie ya existe en
Windows, aquí solo debemos darle el enlace con el .inf para que
conecte correctamente con el firmware que tiene el PIC.
|
USB_STRING_DESC[].
|
La tabla
USB_STRING_DESC contiene la descripción del dispositivo detectado
por el Driver de Windows XP y que nos va a mostrar en la
correspondiente entrada en la lista del Hardware Instalado en el
Sistema.
|
Consta de dos partes o tablas, la
propiamente dicha USB_STRING_DESC que contiene las descripciones
requeridas y una tabla accesoria llamada USB_STRING_DESC_OFFSET
que contiene los offset, o desplazamientos con respecto al inicio de
USB_STRING_DESC en donde se encuentran las correspondientes cadenas.
Ambas constan de tres elementos cada una de ellas.
|
USB_STRING_DESC_OFFSET
tiene tres números que indican cada uno de ellos donde comienza el
correspondiente dato en la tabla USB_STRING_DESC. Así un
contenido de {0,4,12} nos dice que que el primer string comienza en el
byte 0, el segundo en el byte 4 y el tercero se encuentra a partir del
byte número 12. Si cambiamos la longitud de cualquiera de los strings
deberemos reordenar esta tabla correspondientemente con solo contar
los caracteres y apuntar en esta tabla el número de byte donde
comienza cada uno de ellos.
|
USB_STRING_DESC
contiene los tres strings en concreto que deseamos transmitir con el
descriptor USB. Cada uno de ellos tiene la misma estructura que consta
de un primer byte que indica la longitud total de la correspondiente
cadena, un segundo byte que indica el tipo de dato que viene a
continuación y por último tantos bytes como sean necesarios como
contenido del string.
El primer dato de esta tabla es:
4, USB_DES_STRING_TYPE, 0x09, 0x04 que puede leerse como 4 :
Longitud en bytes del dato incluido él mismo. USB_DES_STRING_TYPE que
es una constante cuyo valor e 3 y que dice que lo que sigue es un
string. Y 0x09, 0x04 que le indica al Windows que los strings que
siguen están escritos en correcto ingles americano (US-English)
Los dos siguientes datos son los dos strings que definen nuestro
dispositivo y cuya estructura es idéntica al caso anterior:
8, USB_DES_STRING_TYPE, 'R', 0, 'R', 0, '2', 0 que define el
string el nombre "RR2" de mis Hardware. Total 8 bytes ya que "RR2" se
codifica añadiendo un 0x00 tras cada uno de los caracteres.
Y
22,
USB_DESC_STRING_TYPE, 'R', 0,
'e', 0,
'd', 0,
'P', 0,
'i', 0,
'c', 0,
' ', 0,
'U', 0,
'S', 0,
'B', 0 que define el nombre de mi
dispositivo como "RedPic USB"
|
|
|
|
USB_kbd_HID.h
|
|
|
///////////////////////////////////////////////////////////////////////////
/// usb_kbd_HID.h ////
//// ////
///////////////////////////////////////////////////////////////////////////
#IFNDEF __USB_DESCRIPTORS__
#DEFINE __USB_DESCRIPTORS__
#include <usb.h>
//////////////////////////////////////////////////////////////////
///
/// HID Report. Tells HID driver how to handle and deal with
/// received data. HID Reports can be extremely complex,
/// see HID specifcation for help on writing your own.
///
/// First it defines a block for the first report ID for a mouse
/// protocol. Second it defines a block for the second report ID
/// for a keyboard protocol.
///
//////////////////////////////////////////////////////////////////
const char USB_CLASS_SPECIFIC_DESC[] = {
0x05, 0x01, //usage page (generic desktop) //0,1
0x09, 0x06, //usage (keyboard) //2,3
0xA1, 0x01, //collection (application) //4,5
0x85, 0x02, //report id (2) //6,7
0x05, 0x07, //usage page (key codes) //8,9
0x19, 0xE0, //usage min (224) //10,11
0x29, 0xE7, //usage max (231) //12,13
0x15, 0x00, //logical min (0) //14,15
0x25, 0x01, //logical max (1) //16,17
0x75, 0x01, //report size (1) //18,19
0x95, 0x08, //report count (8) //20, 21
0x81, 0x02, //input (data, variable, absolute) [modifier byte] //22,23
0x95, 0x01, //report count (1) //24,25
0x75, 0x08, //report size (8) //26,27
0x81, 0x01, //input (constant) [reserved byte] //28,29
0x95, 0x05, //report count (5) //30,31
0x75, 0x01, //report size (1) //32,33
0x05, 0x08, //usage page (page# for leds) //34,35
0x19, 0x01, //usage min (1) //36,37
0x29, 0x05, //usage max (5) //38,39
0x91, 0x02, //output (data, var, abs) [led report] //40,41
0x95, 0x01, //report count (1) //42,43
0x75, 0x03, //report size (3) //44,45
0x91, 0x01, //output (constant) [led report padding] //46,47
0x95, 0x05, //report count (5) //48,49
0x75, 0x08, //report size (8) //50,51
0x15, 0x00, //logical min (0) //52,53
0x25, 0x65, //logical max (101) //54,55
0x05, 0x07, //usage page (key codes) //56,57
0x19, 0x00, //usage min (0) //58,59
0x29, 0x65, //usage max (101) //60,61
0x81, 0x00, //input (data, array) //62,63
0xC0 //end collection //64
};
//if a class has an extra descriptor not part of the config descriptor,
// this lookup table defines where to look for it in the const
// USB_CLASS_SPECIFIC_DESC[] array.
//first element is the config number (if your device has more than one
config)
//second element is which interface number
//set element to 0xFFFF if this config/interface combo doesn't exist
const int16 USB_CLASS_SPECIFIC_DESC_LOOKUP[USB_NUM_CONFIGURATIONS][1] =
{
//config 1
//interface 0
0
};
//if a class has an extra descriptor not part of the config descriptor,
// this lookup table defines the size of that descriptor.
//first element is the config number (if your device has more than one
config)
//second element is which interface number
//set element to 0xFFFF if this config/interface combo doesn't exist
const int16 USB_CLASS_SPECIFIC_DESC_LOOKUP_SIZE[USB_NUM_CONFIGURATIONS][2] =
{
//config 1
//interface 0
sizeof(USB_CLASS_SPECIFIC_DESC)
};
//////////////////////////////////////////////////////////////////
///
/// start config descriptor
/// right now we only support one configuration descriptor.
/// the config, interface, class, and endpoint goes into this array.
///
//////////////////////////////////////////////////////////////////
#DEFINE USB_TOTAL_CONFIG_LEN 41 //config+interface+class+endpoint
const char USB_CONFIG_DESC[] = {
//IN ORDER TO COMPLY WITH WINDOWS HOSTS, THE ORDER OF THIS ARRAY MUST BE:
// config(s)
// interface(s)
// class(es)
// endpoint(s)
//config_descriptor for config index 1
USB_DESC_CONFIG_LEN, //length of descriptor size ==0
USB_DESC_CONFIG_TYPE, //constant CONFIGURATION (CONFIGURATION 0x02) ==1
USB_TOTAL_CONFIG_LEN,0, //size of all data returned for this config ==2,3
1, //number of interfaces this device supports ==4
0x01, //identifier for this configuration. (IF we had more than one
configurations) ==5
0x00, //index of string descriptor for this configuration ==6
0xC0, //bit 6=1 if self powered, bit 5=1 if supports remote wakeup (we don't),
bits 0-4 unused and bit7=1 ==7
0x32, //maximum bus power required (maximum milliamperes/2) (0x32 = 100mA)
//8
//interface descriptor 1 (MOUSE)
USB_DESC_INTERFACE_LEN, //length of descriptor =9
USB_DESC_INTERFACE_TYPE, //constant INTERFACE (INTERFACE 0x04) =10
0x00, //number defining this interface (IF we had more than one interface)
==11
0x00, //alternate setting ==12
2, //number of endpoints for this interface //13
0x03, //class code, 03 = HID ==14
0x00, //subclass code //boot ==15
0x00, //protocol code ==16
0x00, //index of string descriptor for interface ==17
//class descriptor 1 (HID)
USB_DESC_CLASS_LEN, //length of descriptor ==18
USB_DESC_CLASS_TYPE, //dscriptor type (0x21 == HID) ==19
0x00,0x01, //hid class release number (1.0) (try 1.10) ==20,21
0x00, //localized country code (0 = none) ==22
0x01, //number of hid class descrptors that follow (1) ==23
0x22, //report descriptor type (0x22 == HID) ==24
USB_CLASS_SPECIFIC_DESC_LOOKUP_SIZE[0][0], 0x00, //length of report
descriptor ==25,26
//endpoint descriptor 1 IN
USB_DESC_ENDPOINT_LEN, //length of descriptor ==27
USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (ENDPOINT 0x05) ==28
0x81, //endpoint number and direction (0x81 = EP1 IN) ==29
USB_ENDPOINT_TYPE_INTERRUPT, //transfer type supported (0x03 is interrupt)
==30
USB_EP1_TX_SIZE,0x00, //maximum packet size supported ==31,32
10 //polling interval, in ms. (cant be smaller than 10 for slow speed
devices) ==33
//endpoint descriptor 1 OUT
USB_DESC_ENDPOINT_LEN, //length of descriptor ==34
USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (ENDPOINT 0x05) ==35
0x01, //endpoint number and direction (0x01 = EP1 OUT) ==36
USB_ENDPOINT_TYPE_INTERRUPT, //transfer type supported (0x03 is interrupt)
==37
USB_EP1_RX_SIZE,0x00, //maximum packet size supported ==38,39
10 //polling interval, in ms. (cant be smaller than 10 for slow speed
devices) ==40
};
//****** BEGIN CONFIG DESCRIPTOR LOOKUP TABLES ********
//since we can't make pointers to constants in certain pic16s, this is an
offset table to find
// a specific descriptor in the above table.
//NOTE: DO TO A LIMITATION OF THE CCS CODE, ALL HID INTERFACES MUST START AT
0 AND BE SEQUENTIAL
// FOR EXAMPLE, IF YOU HAVE 2 HID INTERFACES THEY MUST BE INTERFACE 0 AND
INTERFACE 1
#define USB_NUM_HID_INTERFACES 1
//the maximum number of interfaces seen on any config
//for example, if config 1 has 1 interface and config 2 has 2 interfaces you
must define this as 2
#define USB_MAX_NUM_INTERFACES 1
//define how many interfaces there are per config. [0] is the first config,
etc.
const char USB_NUM_INTERFACES[USB_NUM_CONFIGURATIONS]={1};
//define where to find class descriptors
//first dimension is the config number
//second dimension specifies which interface
//last dimension specifies which class in this interface to get, but most
will only have 1 class per interface
//if a class descriptor is not valid, set the value to 0xFFFF
const int16 USB_CLASS_DESCRIPTORS[USB_NUM_CONFIGURATIONS][1][1]=
{
//config 1
//interface 0
//class 1
18
};
//****** END CONFIG DESCRIPTOR LOOKUP TABLES ********
#if (sizeof(USB_CONFIG_DESC) != USB_TOTAL_CONFIG_LEN)
#error USB_TOTAL_CONFIG_LEN not defined correctly
#endif
//////////////////////////////////////////////////////////////////
///
/// start device descriptors
///
//////////////////////////////////////////////////////////////////
const char USB_DEVICE_DESC[] = {
//starts of with device configuration. only one possible
USB_DESC_DEVICE_LEN, //the length of this report ==1
0x01, //the constant DEVICE (DEVICE 0x01) ==2
0x10,0x01, //usb version in bcd (pic167xx is 1.1) ==3,4
0x00, //class code ==5
0x00, //subclass code ==6
0x00, //protocol code ==7
USB_MAX_EP0_PACKET_LENGTH, //max packet size for endpoint 0. (SLOW SPEED
SPECIFIES 8) ==8
0x61,0x04, //vendor id (0x04D8 is Microchip, or is it 0x0461 ??)
0x57,0x00, //product id ==11,12 //don't use ffff says usb-by-example guy.
oops
0x00,0x01, //device release number ==13,14
0x01, //index of string description of manufacturer. therefore we point to
string_1 array (see below) ==15
0x02, //index of string descriptor of the product ==16
0x00, //index of string descriptor of serial number ==17
USB_NUM_CONFIGURATIONS //number of possible configurations ==18
};
#if (sizeof(USB_DEVICE_DESC) != USB_DESC_DEVICE_LEN)
#error USB_DESC_DEVICE_LEN not defined correctly
#endif
//////////////////////////////////////////////////////////////////
///
/// start string descriptors
/// String 0 is a special language string, and must be defined. People in
U.S.A. can leave this alone.
///
//////////////////////////////////////////////////////////////////
//the offset of the starting location of each string. offset[0] is the start
of string 0, offset[1] is the start of string 1, etc.
const char USB_STRING_DESC_OFFSET[]={0,4,12};
//number of strings you have, including string 0.
#define USB_STRING_DESC_COUNT sizeof(USB_STRING_DESC_OFFSET)
char const USB_STRING_DESC[]={
//string 0
4, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
0x09,0x04, //Microsoft Defined for US-English
//string 1
8, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
'R',0,
'R',0,
'2',0,
//string 2
46, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
'U',0,
'S',0,
'B',0,
' ',0,
'K',0,
'e',0,
'y',0,
'b',0,
'o',0,
'a',0,
'r',0,
'd',0,
' ',0,
'b',0,
'y',0,
' ',0,
'R',0,
'e',0,
'd',0,
'p',0,
'i',0,
'c',0
};
|
|
|
|
|
|
|
|
-
<>
Detectando Windows nuestro dispositivo
|
Si conectamos al USB
del PC nuestro PIC debe éste detectarlo como un Keyboard Estándar:
|
|
El firmware tiene habilitado un push-button
que cada vez que se pulsa genera un carácter del '0' al '9' y de la 'a' a
la 'z' que es transmitido como una pulsación de un teclado normal USB:
|
|
Recursos, todo lo necesario. |
|
|
Aquí tenéis todo los
necesario para este proyecto: El Firmware
para el 18F4550, .hex y el .c para el CCS C,
|
|
Nota: RRUSB_Complete_zip incluye
también los recursos para los demás métodos de conexión USB descritos en
El USB desencadenado
|
|
Enlaces imprescindibles. |
|
|
|
|
|
|
|
|
Esta página se modificó el
27/12/2008
|