1. LIBRERÍAS DEL USB Las librerías que aquí aparecen son un ejemplo. Dependiendo de la clase (HID, MSD, GEN, BOOT o CDC) hay algunas variaciones. En rojo aparecen los datos que hay que cambiar en cada firmware. En azul las líneas que dependen de la clase. Y en verde la parte de la librería que es código, aunque el texto en rojo y en azul también es código. Para realizar una conexión sin características específicas, lo recomendable es utilizar la clase genérica (GEN) ya que permite una velocidad más alta que la clase CDC, aunque esta última se utiliza por resultar más fácil la programación del host. Para ello se copian todas las librerías de esta clase que suministra Microchip y se modifican las líneas que aquí aparecen en rojo. Lo más importante es definir bien las interfaces y los Endpoints. Para realizar una aplicación en una clase determinada, lo más sencillo es copiar todas las librerías de esa clase, corregirlas y modificar las librerías user.h y las aquí comentadas. Haciendo esto, nos evitamos tener que modificar el código al cambiar la clase del dispositivo. Para más información visitar la página web de microchip, en la que hay ejemplos de cada clase. 1.1.1. USB.H Esta librería proporciona la forma de incluir todos los archivos necesarios del firmware del USB de Microchip. En realidad, es la única librería que hay que incluir en el programa de inicio, ya que se encarga de añadir las demás. El orden de inclusión es importante, ya que se resuelven los conflictos de dependencia con el orden correcto: #include "autofiles\usbcfg.h" #include "system\usb\usbdefs\usbdefs_std_dsc.h" #include "autofiles\usbdsc.h" #include "system\usb\usbdefs\usbdefs_ep0_buff.h" #include "system\usb\usbmmap.h" #include "system\usb\usbdrv\usbdrv.h" #include "system\usb\usbctrltrf\usbctrltrf.h" #include "system\usb\usb9\usb9.h" Si USB_USE_HID está definida se incluye la librería hid.h #if defined(USB_USE_HID) #include "system\usb\class\hid\hid.h" #endif Ver autofiles\usbcfg.h Documento creado por Slalen para Electronics Strange World Si USB_USE_MSD está definida se incluye la librería msd.h #if defined(USB_USE_MSD) #include "system\usb\class\msd\msd.h" #endif Ver autofiles\usbcfg.h Si USB_USE_CDC está definida se incluye la librería cdc.h #if defined(USB_USE_CDC) #include "system\usb\class\cdc\cdc.h" #endif Ver autofiles\usbcfg.h 2 Librerías C18 del USB 1.1.2. USBCFG.H: CONFIGURACIÓN Esta librería es la encargada de la configuración del buffer por defecto, del Endpoint 0, de los Endpoints utilizados en la comunicación… Hay que modificarla en cada aplicación. 1.1.2.1. Definiciones Tamaño del buffer del Endpoint 0 (8, 16 32 ó 64): #define EP0_BUFF_SIZE 8 Número máximo de interrupciones: #define MAX_NUM_INT 1 Definición de parámetros descritos en usbdrv.h: Modo Ping-Pong: #define MODE_PP _PPBM0 Valor de configuración: #define UCFG_VAL _PUEN|_TRINT|_FS|MODE_PP E/S auto-alimentadas: #define USE_SELF_POWER_SENSE_IO E/S dependientes del bus USB: #define USE_USB_BUS_SENSE_IO Las dos definiciones anteriores se realizarán cuando sea necesario, siendo totalmente independiente una de la otra. 1.1.2.2. Uso de la clase del dispositivo Dependiendo de la aplicación hay que definir la clase que vamos a utilizar. Uso de la interfaz humana del dispositivo USB: #define USB_USE_HID Uso del CDC del dispositivo: #define USB_USE_CDC 9 Uso de la clase Almacenamiento masivo: #define USB_USE_MSD 9 9 MUID = Microchip USB Clase ID Se utiliza para identificar la clase del USB de la sesión actual de control de la transferencia del EP0: #define MUID_NULL #define MUID_USB9 #define MUID_HID #define MUID_CDC 1.1.2.3. 0 1 2 3 Ninguna USB9 Interfaz humana Clase de comunicación del dispositivo Distribución de los Endpoint #define HID_INTF_ID #define HID_UEP 0x00 UEP1 Identificación de la interfaz Endpoints que se utlizan 3 Documento creado por Slalen para Electronics Strange World #define #define #define #define HID_BD_OUT HID_INT_OUT_EP_SIZE HID_BD_IN HID_INT_IN_EP_SIZE #define HID_NUM_OF_DSC #define HID_RPT01_SIZE ep1Bo 3 ep1Bi 3 1 50 Buffer descriptor de salida Tamaño de la interrupción del Endpoint de salida Buffer descriptor de entrada Tamaño de la interrupción del Endpoint de entrada Número de descriptores Tamaño del informe Macros HID Ver la dirección del descriptor HID: #define mUSBGetHIDDscAdr(ptr) { if(usb_active_cfg == 1) ptr = (rom byte*)&cfg01.hid_i00a00; } Ver la dirección del informe del descriptor HID: #define mUSBGetHIDRptDscAdr(ptr) { if(usb_active_cfg == 1) ptr = (rom byte*)&hid_rpt01; } Ver el tamaño del informe del descriptor HID: #define mUSBGetHIDRptDscSize(count) { if(usb_active_cfg == 1) count = sizeof(hid_rpt01); } Número máximo de Endpoints: #define MAX_EP_NUMBER 1 //UEP1 4 Librerías C18 del USB 1.1.3. USBDEFS_STD_DSC.H: DESCRIPTORES: DEFINICIONES ESTÁNDAR DE LOS Mediante esta libraría se crean las estructuras de los descriptores estándar, se definen constantes para realizar el código más rápidamente, etc. En general, no hay que modificar nada de la librería. En nuestra aplicación copiaremos la que cede Microchip que corresponda con nuestra clase. 1.1.3.1. Incluye: #include "system\typedefs.h" 1.1.3.2. Librería en la que se definen tipos de datos Definiciones Tipos de descriptores: #define DSC_DEV #define DSC_CFG #define DSC_STR #define DSC_INTF #define DSC_EP 0x01 0x02 0x03 0x04 0x05 Descriptor del dispositivo Descriptor de configuración Descriptor de la secuencia Descriptor de la interfaz Descriptor del Endpoint Definición de los Endpoint (sólo usarse con los descriptores, para cualquier otro uso utilizar los definidos en usbdrv.h): #define _EP01_OUT #define _EP01_IN #define _EP02_OUT #define _EP02_IN #define _EP03_OUT #define _EP03_IN #define _EP04_OUT #define _EP04_IN #define _EP05_OUT #define _EP05_IN #define _EP06_OUT #define _EP06_IN #define _EP07_OUT #define _EP07_IN #define _EP08_OUT #define _EP08_IN #define _EP09_OUT #define _EP09_IN #define _EP10_OUT #define _EP10_IN #define _EP11_OUT #define _EP11_IN #define _EP12_OUT #define _EP12_IN #define _EP13_OUT #define _EP13_IN 0x01 0x81 0x02 0x82 0x03 0x83 0x04 0x84 0x05 0x85 0x06 0x86 0x07 0x87 0x08 0x88 0x09 0x89 0x0A 0x8A 0x0B 0x8B 0x0C 0x8C 0x0D 0x8D 5 Documento creado por Slalen para Electronics Strange World #define _EP14_OUT #define _EP14_IN #define _EP15_OUT #define _EP15_IN 0x0E 0x8E 0x0F 0x8F Atributos de configuración: #define _DEFAULT #define _SELF #define _RWU 0x01<<7 0x01<<6 0x01<<5 Valor por defecto (El Bit 7 se activa) Auto-alimentado (Mantener si está activo) Reinicio remoto (Mantener si está activo) Tipo de transferencia del Endpoint: #define _CTRL #define _ISO #define _BULK #define _INT 0x00 0x01 0x02 0x03 Transferencia de control Transferencia síncrona Transferencia Bulk Transferencia interrupción Tipo de sincronización del Endpoint de transferencia síncrona: #define _NS #define _AS #define _AD #define _SY 0x00<<2 0x01<<2 0x02<<2 0x03<<2 Sin sincronización Asíncrona Adaptivo Síncrona Utilización del Endpoint tipo síncrono: #define _DE #define _FE #define _IE 1.1.3.3. 0x00<<4 0x01<<4 0x02<<4 Endpoint de datos Endpoint retroalimentado Endpoint de datos y retroalimentado Estructuras Estructura de los descriptores del dispositivo USB (ver usbdsc.c): typedef struct _USB_DEV_DSC { Longitud; Tipo de descriptor; byte bLength; byte bDscType; Clase del dispositivo; Subclase del dispositivo; byte bDevCls; byte bDevSubCls; Tamaño máximo del paquete; Identificador del fabricante; producto; byte bMaxPktSize0; word idVendor; Bcd del dsipositivo; Información de la manufactura; word bcdDevice; byte iMFR; Número de serie; Número de configuración; byte iSerialNum; byte bNumCfg; } USB_DEV_DSC; Bcd del USB; word bcdUSB; Protocolo del dispositivo; byte bDevProtocol; Identificación del word idProduct; Información del producto; byte iProduct; Estructura del descriptor de configuración del USB: typedef struct _USB_CFG_DSC { Longitud; Tipo de descriptor; byte bLength; byte bDscType; Número de interfaz; Valor de configuración; configuración; 6 Longitud total; word wTotalLength; Información de Librerías C18 del USB byte bNumIntf; Atributos; byte bmAttributes; } USB_CFG_DSC; byte bCfgValue; Máxima energía; byte bMaxPower; byte iCfg; Estructura de la interfaz del dispositivo USB: typedef struct _USB_INTF_DSC { Longitud; Tipo de descriptor; byte bLength; byte bDscType; Configuración alterna; Número de Endpoints; byte bAltSetting; byte bNumEPs; Subclase de interfaz; Protocolo de la interfaz; byte bIntfSubCls; byte bIntfProtocol; } USB_INTF_DSC; Número de interfaz; byte bIntfNum; Clase de interfaz; byte bIntfCls; Información de la interfaz; byte iIntf; Estructura del Descriptor del Endpoint del USB: typedef struct _USB_EP_DSC { Longitud; byte bLength; Atributos; byte bmAttributes; } USB_EP_DSC; Tipo de descriptor; byte bDscType; Tamaño máximo del paquete; word wMaxPktSize; 7 Dirección del Endpoint; byte bEPAdr; Intervalo; byte bInterval; Documento creado por Slalen para Electronics Strange World 1.1.4. USBDSC.H: DESCRIPTORES Esta es una de las librerías más importantes, ya que en ella se definen los descriptores. Como hemos visto anteriormente, los descriptores definen la interfaz, la clase, el fabricante, etc. Esta librería suele ser totalmente distinta en cada aplicación. 1.1.4.1. Librerías que incluye #include "system\typedefs.h" #include "autofiles\usbcfg.h" #include "system\usb\usb.h" 1.1.4.2. Definiciones #define CFG01 rom struct { USB_CFG_DSC cd01; USB_INTF_DSC i00a00; USB_EP_DSC ep01o_i00a00; USB_EP_DSC ep01i_i00a00; } cfg01 1.1.4.3. Configuración 01 Descriptor de configuración Descriptor de la interfaz Endpoint descriptor de salida Endpoint descriptor de entrada Externas extern rom USB_DEV_DSC device_dsc; extern CFG01; extern rom struct{byte bLength;byte bDscType;word string[1];}sd000; 8 Librerías C18 del USB 1.1.5. USBDEFS_EP0_BUFF.H: ENDPOINT 0 DESCRIPCIONES DEL BUFFER DEL La librería actual recopila las características del Endpoint 0 y de su buffer, no hay que modificarla ya que las variables se han declarado en otras librerías. 1.1.5.1. Incluye #include "system\typedefs.h" #include "autofiles\usbcfg.h" Control de transferencias setup Cada paquete setup tiene 8bytes. Sin embargo, el tamaño del buffer del Endpoint 0 es el especificado en la librería usbcfg.h. El tamaño del buffer puede ser de 8, 16, 32 ó 64. Los primeros 8bytes se definen para direccionarse directamente para mejorar la velocidad y reducir el tamaño del código. El resto de bytes se direccionan indirectamente. typedef union _CTRL_TRF_SETUP { Matriz para el direccionamiento indirecto struct { byte _byte[EP0_BUFF_SIZE]; }; Respuestas estándar del dispositivo struct { byte bmRequestType; byte bRequest; word wValue; word wIndex; word wLength; }; struct { unsigned :8; unsigned :8; WORD W_Value; WORD W_Index; WORD W_Length; }; struct { unsigned Recipient:5; unsigned RequestType:2; unsigned DataDir:1; unsigned :8; Tipo de respuesta Respuesta Valor Índice Longitud Valor Índice Longitud Dispositivo, Interfaz, Endpoint, Otro Estándar, Clase, Fabricante, Reservado Host-al-dispositivo, Dispositivo-al-host 9 Documento creado por Slalen para Electronics Strange World byte bFeature; unsigned :8; unsigned :8; unsigned :8; unsigned :8; unsigned :8; }; struct { unsigned :8; unsigned :8; byte bDscIndex; byte bDscType; word wLangID; unsigned :8; unsigned :8; }; struct { unsigned :8; unsigned :8; BYTE bDevADR; byte bDevADRH; unsigned :8; unsigned :8; unsigned :8; unsigned :8; }; struct { unsigned :8; unsigned :8; byte bCfgValue; byte bCfgRSD; unsigned :8; unsigned :8; unsigned :8; unsigned :8; }; struct { unsigned :8; unsigned :8; byte bAltID; byte bAltID_H; byte bIntfID; byte bIntfID_H; unsigned :8; unsigned :8; }; Reinicio remoto, Paro del Endpoint Sólo para configuración y String del descriptor Dispositivo, Configuración, String Identificación de idioma Dirección del dispositivo 0-127 Tiene que ser cero Valor de configuración 0-255 Tiene que ser cero (Reservado) Valor alterno de configuración 0-255 Tiene que ser cero Valor del número de interfaz 0-255 Tiene que ser cero 10 Librerías C18 del USB struct { unsigned :8; unsigned :8; unsigned :8; unsigned :8; byte bEPID; byte bEPID_H; unsigned :8; unsigned :8; }; struct { unsigned :8; unsigned :8; unsigned :8; unsigned :8; unsigned EPNum:4; unsigned :3; unsigned EPDir:1; unsigned :8; unsigned :8; unsigned :8; }; } CTRL_TRF_SETUP; Identificación del Endpoint ID (Número y Dirección) Tiene que ser cero Número del Endpoint 0-15 Dirección del Endpoint: 0-OUT, 1-IN Control de transferencia de datos typedef union _CTRL_TRF_DATA { Matriz para el direccionamiento indirecto: struct { byte _byte[EP0_BUFF_SIZE]; }; Los primeros 8bytes direccionables directamente: struct { byte _byte0; byte _byte1; byte _byte2; byte _byte3; 11 Documento creado por Slalen para Electronics Strange World byte _byte4; byte _byte5; byte _byte6; byte _byte7; }; struct { word _word0; word _word1; word _word2; word _word3; }; } CTRL_TRF_DATA; 12 Librerías C18 del USB 1.1.6. USBMMAP.H Esta librería define constantes y estructuras utilizadas por otras. El programador, salvo excepción, no tiene que modificarla utilizándola tal y como aparece. 1.1.6.1. Incluye #include "system\typedefs.h" 1.1.6.2. Definiciones Parámetros de inicialización del descriptor del registro estado: #define _BSTALL #define _DTSEN #define _INCDIS #define _KEN 0x04 0x08 0x10 0x20 #define _DAT0 #define _DAT1 #define _DTSMASK #define _USIE #define _UCPU 0x00 0x40 0x40 0x80 0x00 Parada del buffer activa Dato de sincronización activo Incremento de dirección desactivado Guardado del buffer descriptor por el SIE activo Paquete DATA0 esperando el siguiente Paquete DATA1 esperando el siguiente Máscara DTS El SIE controla el buffer La CPU controla el buffer Estados del dispositivo USB. Para utilizarlos con [byte usb_device_state]: #define DETACHED_STATE #define ATTACHED_STATE #define POWERED_STATE #define DEFAULT_STATE #define ADR_PENDING_STATE #define ADDRESS_STATE #define CONFIGURED_STATE Tipos de memoria USB_DEVICE_STATUS: 0 1 2 3 4 5 6 para Sin conexión Conectado Alimentado Por defecto Pendiente de dirección Direccionado Configurado el control de transferencias, #define _RAM 0 #define _ROM 1 1.1.6.3. Tipos typedef union _USB_DEVICE_STATUS { byte _byte; struct Estado del dispositivo 13 utilizado en Documento creado por Slalen para Electronics Strange World { unsigned RemoteWakeup:1; unsigned ctrl_trf_mem:1; }; } USB_DEVICE_STATUS; [0]Desactivado [1]Activado: Ver usbdrv.h, usb9.h [0]RAM [1]ROM typedef union _BD_STAT { byte _byte; struct{ unsigned BC8:1; unsigned BC9:1; unsigned BSTALL:1; unsigned DTSEN:1; unsigned INCDIS:1; unsigned KEN:1; unsigned DTS:1; unsigned UOWN:1; }; struct{ unsigned BC8:1; unsigned BC9:1; unsigned PID0:1; unsigned PID1:1; unsigned PID2:1; unsigned PID3:1; unsigned :1; unsigned UOWN:1; }; struct{ unsigned :2; unsigned PID:4; unsigned :2; }; } BD_STAT; Estado del buffer descriptor typedef union _BDT { struct { BD_STAT Stat; byte Cnt; byte ADRL; byte ADRH; }; struct { unsigned :8; unsigned :8; Tabla del buffer descriptor Parada del buffer activa Dato de sincronización activo Incremento de dirección desactivado Guardado del buffer descriptor por el SIE activo Valor del dato de sincronización Propiedad del USB Paquete de identificación Dirección del buffer baja Dirección del buffer alta 14 Librerías C18 del USB byte* ADR; }; } BDT; 1.1.6.4. Dirección del Buffer Externas extern byte usb_device_state; extern USB_DEVICE_STATUS usb_stat; extern byte usb_active_cfg; extern byte usb_alt_intf[MAX_NUM_INT]; extern volatile far BDT ep0Bo; extern volatile far BDT ep0Bi; extern volatile far BDT ep1Bo; extern volatile far BDT ep1Bi; extern volatile far BDT ep2Bo; extern volatile far BDT ep2Bi; extern volatile far BDT ep3Bo; extern volatile far BDT ep3Bi; extern volatile far BDT ep4Bo; extern volatile far BDT ep4Bi; extern volatile far BDT ep5Bo; extern volatile far BDT ep5Bi; extern volatile far BDT ep6Bo; extern volatile far BDT ep6Bi; extern volatile far BDT ep7Bo; extern volatile far BDT ep7Bi; extern volatile far BDT ep8Bo; extern volatile far BDT ep8Bi; extern volatile far BDT ep9Bo; extern volatile far BDT ep9Bi; extern volatile far BDT ep10Bo; extern volatile far BDT ep10Bi; extern volatile far BDT ep11Bo; extern volatile far BDT ep11Bi; extern volatile far BDT ep12Bo; extern volatile far BDT ep12Bi; extern volatile far BDT ep13Bo; extern volatile far BDT ep13Bi; extern volatile far BDT ep14Bo; extern volatile far BDT ep14Bi; extern volatile far BDT ep15Bo; extern volatile far BDT ep15Bi; Buffer descriptor del Endpoint #0 Out Buffer descriptor del Endpoint #0 In Buffer descriptor del Endpoint #1 Out Buffer descriptor del Endpoint #1 In Buffer descriptor del Endpoint #2 Out Buffer descriptor del Endpoint #2 In Buffer descriptor del Endpoint #3 Out Buffer descriptor del Endpoint #3 In Buffer descriptor del Endpoint #4 Out Buffer descriptor del Endpoint #4 In Buffer descriptor del Endpoint #5 Out Buffer descriptor del Endpoint #5 In Buffer descriptor del Endpoint #6 Out Buffer descriptor del Endpoint #6 In Buffer descriptor del Endpoint #7 Out Buffer descriptor del Endpoint #7 In Buffer descriptor del Endpoint #8 Out Buffer descriptor del Endpoint #8 In Buffer descriptor del Endpoint #9 Out Buffer descriptor del Endpoint #9 In Buffer descriptor del Endpoint #10 Out Buffer descriptor del Endpoint #10 In Buffer descriptor del Endpoint #11 Out Buffer descriptor del Endpoint #11 In Buffer descriptor del Endpoint #12 Out Buffer descriptor del Endpoint #12 In Buffer descriptor del Endpoint #13 Out Buffer descriptor del Endpoint #13 In Buffer descriptor del Endpoint #14 Out Buffer descriptor del Endpoint #14 In Buffer descriptor del Endpoint #15 Out Buffer descriptor del Endpoint #15 In extern volatile far CTRL_TRF_SETUP SetupPkt; extern volatile far CTRL_TRF_DATA CtrlTrfData; #if defined(USB_USE_HID) Si está definido USB_USE_HID extern volatile far unsigned char hid_report_out[HID_INT_OUT_EP_SIZE]; extern volatile far unsigned char hid_report_in[HID_INT_IN_EP_SIZE]; #endif 15 Documento creado por Slalen para Electronics Strange World 1.1.7. USBDRV.H: DRIVER DEL USB Esta librería es la encargada de definir la configuración inicial. Lo único que hay que modificar es la corrección de la errata en la original de Microchip, como se comenta más adelante. 1.1.7.1. Incluye #include "system\typedefs.h" #include "system\usb\usb.h" 1.1.7.2. Definiciones Parámetros de configuración iniciales: #define _PPBM0 #define _PPBM1 #define _PPBM2 #define _LS #define _FS #define _TRINT #define _TREXT #define _PUEN #define _OEMON #define _UTEYE 0x00 0x01 0x02 0x00 0x04 0x00 0x08 0x10 0x40 0x80 Buffer Ping-pong Modo 0 Buffer Ping-pong Modo 1 Buffer Ping-pong Modo 2 Modo USB Low-Speed Modo USB Full-Speed Transmisor-receptor interno Transmisor-receptor externo Usar resistencias pull-up internas Usar el indicador de salida SIE Usar el test “Patrón de ojo” Parámetros de los Endpoint iniciales: #define EP_CTRL #define EP_OUT #define EP_IN #define EP_OUT_IN #define HSHK_EN 0x06 Pipe de control 0x0C Pipe de salida 0x0A Pipe de entrada 0x0E Pipe de entrada y salida 0x10 Activar paquetes de protocolo Los paquetes de protocolo se tienen que desactivar en la síncronas Definiciones de los Endpoints PICmicro El formato de la X:EP3:EP2:EP1:EP0:DIR:PPBI:X dirección de los EP PICmicro: Esto se utiliza cuando se comprueba el valor leído de la USTAT. NOTA: estas definiciones no se usan en los descriptores porque tienen distinto formato. Se definen en : "system\usb\usbdefs\usbdefs_std_dsc.h" #define OUT #define IN 0 1 #define PIC_EP_NUM_MASK 0b01111000 #define PIC_EP_DIR_MASK 0b00000100 Número de máscara del Endpoint Dirección de la máscara del Endpoint 16 Librerías C18 del USB NOTA: la librería tiene una errata en la definición de los Endpoints, lo correcto es: #define EP00_OUT #define EP00_IN #define EP01_OUT #define EP01_IN #define EP02_OUT #define EP02_IN #define EP03_OUT #define EP03_IN #define EP04_OUT #define EP04_IN #define EP05_OUT #define EP05_IN #define EP06_OUT #define EP06_IN #define EP07_OUT #define EP07_IN #define EP08_OUT #define EP08_IN #define EP09_OUT #define EP09_IN #define EP10_OUT #define EP10_IN #define EP11_OUT #define EP11_IN #define EP12_OUT #define EP12_IN #define EP13_OUT #define EP13_IN #define EP14_OUT #define EP14_IN #define EP15_OUT #define EP15_IN ((0x00<<3)|(OUT<<2)) ((0x00<<3)|(IN<<2)) ((0x01<<3)|(OUT<<2)) ((0x01<<3)|(IN<<2)) ((0x02<<3)|(OUT<<2)) ((0x02<<3)|(IN<<2)) ((0x03<<3)|(OUT<<2)) ((0x03<<3)|(IN<<2)) ((0x04<<3)|(OUT<<2)) ((0x04<<3)|(IN<<2)) ((0x05<<3)|(OUT<<2)) ((0x05<<3)|(IN<<2)) ((0x06<<3)|(OUT<<2)) ((0x06<<3)|(IN<<2)) ((0x07<<3)|(OUT<<2)) ((0x07<<3)|(IN<<2)) ((0x08<<3)|(OUT<<2)) ((0x08<<3)|(IN<<2)) ((0x09<<3)|(OUT<<2)) ((0x09<<3)|(IN<<2)) ((0x0A<<3)|(OUT<<2)) ((0x0A<<3)|(IN<<2)) ((0x0B<<3)|(OUT<<2)) ((0x0B<<3)|(IN<<2)) ((0x0C<<3)|(OUT<<2)) ((0x0C<<3)|(IN<<2)) ((0x0D<<3)|(OUT<<2)) ((0x0D<<3)|(IN<<2)) ((0x0E<<3)|(OUT<<2)) ((0x0E<<3)|(IN<<2)) ((0x0F<<3)|(OUT<<2)) ((0x0F<<3)|(IN<<2)) mInitializeUSBDriver() Configura el modulo USB. La definición de UCFG_VAL está en autofiles\usbcfg.h Este registro determina: velocidad del USB Speed, selección de las resistencias pull-up del chip, selección del transmisor-receptor del chip, modo de chequeo “patrón de ojo”, buffer modo Ping-pong 17 Documento creado por Slalen para Electronics Strange World #define mInitializeUSBDriver() {UCFG = UCFG_VAL; usb_device_state = DETACHED_STATE; usb_stat._byte = 0x00; usb_active_cfg = 0x00; } void mDisableEP1to15() Esta macro desactiva todos los Endpoints menos el 0. Hay que invocar esta macro cada vez que el host envíe una señal de RESET o una respuesta a SET_CONFIGURATION #define mDisableEP1to15() ClearArray((byte*)&UEP1,15); O lo que es lo mismo: #define mDisableEP1to15() UEP1=0x00;UEP2=0x00;UEP3=0x00; UEP4=0x00;UEP5=0x00;UEP6=0x00;UEP7=0x00; UEP8=0x00;UEP9=0x00;UEP10=0x00;UEP11=0x00; UEP12=0x00;UEP13=0x00;UEP14=0x00;UEP15=0x00; mUSBBufferReady(buffer_dsc) Precondición: Endpoint IN: El buffer está cargado y listo para enviar. Endpoint OUT: El buffer puede escribir al SIE. Entrada: byte buffer_dsc: Nombre del grupo del buffer descriptor (e.j. ep0Bo, ep1Bi) declarado en usbmmap.h. Los nombres se pueden cambiar por legibilidad; ver los ejemplos en usbcfg.h (#define HID_BD_OUT ep1Bo) Esta macro se tiene que llamar cada vez que ocurra: 1. Que se llene un buffer de un Endpoint, que no sea el EP0, con datos. 2. Que se lea un buffer de un Endpoint, que no sea el EP0. Esta macro convierte la propiedad del buffer al SIE para dar servicio; además, cambia el bit DTS para sincronización. #define mUSBBufferReady(buffer_dsc) { buffer_dsc.Stat._byte &= _DTSMASK; buffer_dsc.Stat.DTS = !buffer_dsc.Stat.DTS; buffer_dsc.Stat._byte |= _USIE|_DTSEN; } 1.1.7.3. Prototipos públicos void USBCheckBusStatus(void); void USBDriverService(void); void USBRemoteWakeup(void); void USBSoftDetach(void); void ClearArray(byte* startAdr,byte count); 18 Guarda sólo el bit DTS Cambia el bit DTS Cambia la propiedad al SIE Librerías C18 del USB 1.1.8. USBCTRLTRF.H: CONTROL DE TRANSFERENCIAS DEL USB Esta librería es la encargada de definir los tipos de transferencia. Se utiliza desde otras librerías al declarar la interfaz, la transferecia, etc. No hay que modificarla 1.1.8.1. Incluye #include "system\typedefs.h" 1.1.8.2. Definiciones Estado de las transferencias de control #define WAIT_SETUP #define CTRL_TRF_TX #define CTRL_TRF_RX 0 1 2 Espera Setup Transf. de control de transmisión Transf. de control de recepción 0b00001101 0b00000001 0b00001001 Token setup Token de salida Token de entrada Tipos de Tokens: #define SETUP_TOKEN #define OUT_TOKEN #define IN_TOKEN Definición de los tipos de respuesta: #define HOST_TO_DEV #define DEV_TO_HOST #define STANDARD #define CLASS #define VENDOR #define RCPT_DEV #define RCPT_INTF #define RCPT_EP #define RCPT_OTH 1.1.8.3. 0 1 0x00 0x01 0x02 0 1 2 3 Host-al-Dispositivo Dispositivo-al-Host Clase Fabricante Dispositivo destinatario Destinatario de la interfaz Destinatario del Endpoint Externas extern byte ctrl_trf_session_owner; extern POINTER pSrc; extern POINTER pDst; extern WORD wCount; 1.1.8.4. Prototipos públicos void USBCtrlEPService(void); void USBCtrlTrfTxService(void); void USBCtrlTrfRxService(void); void USBCtrlEPServiceComplete(void); void USBPrepareForNextSetupTrf(void); 19 Documento creado por Slalen para Electronics Strange World 1.1.9. USB9.H Gracias a esta librería obtenemos la dirección del dispositivo, entre otras características. No se tiene que modificar. 1.1.9.1. Incluye #include "system\typedefs.h" 1.1.9.2. Definiciones Códigos de respuesta estándar: #define GET_STATUS #define CLR_FEATURE #define SET_FEATURE #define SET_ADR #define GET_DSC #define SET_DSC #define GET_CFG #define SET_CFG #define GET_INTF #define SET_INTF #define SYNCH_FRAME 0 1 3 5 6 7 8 9 10 11 12 Obtiene estado Borra característica Fija característica Fija dirección Obtiene descriptor Fija descriptor Obtiene configuración Fija configuración Obtiene interfaz Fija interfaz Marco de sincronismo Características de los selectores estándar: #define DEVICE_REMOTE_WAKEUP #define ENDPOINT_HALT 0x01 0x00 Reinicio remoto del dispositivo Paro del Endpoint mUSBCheckAdrPendingState() Rutina de chequeo especializado, comprueba si el dispositivo está en el estado “Pendiente de dirección” y le da servicio si está. #define mUSBCheckAdrPendingState() if(usb_device_state==ADR_PENDING_STATE) { UADDR = SetupPkt.bDevADR._byte; if(UADDR > 0) usb_device_state=ADDRESS_STATE; else usb_device_state=DEFAULT_STATE; } 1.1.9.3. Prototipos públicos void USBCheckStdRequest(void); 20 Librerías C18 del USB 1.1.10. USBGEN.H: USB GENÉRICO Esta es la librería de la clase genérica. Se definen las transferencias creando funciones para realizarlas. No hay que modificarla. 1.1.10.1. Incluye #include "system\typedefs.h" 1.1.10.2. Definiciones (bit) mUSBGenRxIsBusy(void) Esta macro se utiliza para comprobar que el Endpoint de salida está ocupado (lo controla el SIE) o no. Uso típico: if(mUSBGenRxIsBusy()) #define mUSBGenRxIsBusy() USBGEN_BD_OUT.Stat.UOWN (bit) mUSBGenTxIsBusy(void) Esta macro se utiliza para comprobar que el Endpoint de entrada está ocupado (lo controla el SIE) o no. Uso típico: if(mUSBGenTxIsBusy()) #define mUSBGenTxIsBusy() USBGEN_BD_IN.Stat.UOWN byte mUSBGenGetRxLength(void) Salida: mUSBGenGetRxLength devuelve usbgen_rx_len (longitude de Rx). mUSBGenGetRxLength se utiliza para recuperar el número de bytes copiados al buffer del usuario en la última llamada a la función USBGenRead. #define mUSBGenGetRxLength() 1.1.10.3. usbgen_rx_len Externas extern byte usbgen_rx_len; 1.1.10.4. Prototipos Públicos void USBGenInitEP(void); void USBGenWrite(byte *buffer, byte len); byte USBGenRead(byte *buffer, byte len); 21 Documento creado por Slalen para Electronics Strange World 1.1.11. MSD.H: ALMACENAMIENTO MASIVO Esta es la librería de la clase MSD. Se definen las transferencias creando funciones para realizarlas. No hay que modificarla. 1.1.11.1. Incluye #include "system\typedefs.h" #include "io_cfg.h" 1.1.11.2. Mapa de pines E/S Definiciones Código de la clase de interfaz MSD: #define MSD_INTF 0x08 Código de la subclase de la interfas de clase MSD: #define MSD_INTF_SUBCLASS 0x06 Código de protocolo de la clase de la interfaz MSD: #define MSD_PROTOCOL 0x50 Comandos de la clase: #define MSD_RESET #define GET_MAX_LUN 0xff 0xfe #define BLOCKLEN_512 0x0200 #define STMSDTRIS #define STRUNTRIS #define STMSDLED #define STRUNLED #define ToggleRUNLED() TRISD0 TRISD1 LATDbits.LATD0 LATDbits.LATD1 STRUNLED = !STRUNLED; Set de commandos de código de la subclase transparente SCSI: #define INQUIRY #define READ_FORMAT_CAPACITY #define READ_CAPACITY #define READ_10 #define WRITE_10 #define REQUEST_SENSE #define MODE_SENSE #define PREVENT_ALLOW_MEDIUM_REMOVAL #define TEST_UNIT_READY #define VERIFY #define STOP_START 0x12 0x23 0x25 0x28 0x2A 0x03 0x1A 0x1E 0x00 0x2F 0x1B Varios estados del Firmware de almacenamiento masivo: #define MSD_WAIT 0 #define MSD_DATA_IN 2 #define MSD_DATA_OUT 3 Esperando para un CBW válido Estado de datos IN (Dispositivo-> Host) Estado de datos OUT (Host -> Device) 22 Librerías C18 del USB #define MSD_CSW_SIZE 0x0d #define MSD_CBW_SIZE 0x1f #define INVALID_CBW #define VALID_CBW #define MAX_LUN Datos CSW de 10 bytes CSW Datos CSW de 31 bytes CBW 1 !INVALID_CBW 0 Clave de los códigos de error Sense #define S_NO_SENSE #define S_RECOVERED_ERROR #define S_NOT_READY #define S_MEDIUM_ERROR #define S_HARDWARE_ERROR #define S_ILLEGAL_REQUEST #define S_UNIT_ATTENTION #define S_DATA_PROTECT #define S_BLANK_CHECK #define S_VENDOR_SPECIFIC #define S_COPY_ABORTED #define S_ABORTED_COMMAND #define S_OBSOLETE #define S_VOLUME_OVERFLOW #define S_MISCOMPARE 0x0 0x1 0x2 0x3 0X4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE #define S_CURRENT #define S_DEFERRED 0x70 0x71 Códigos ASC ASCQ para datos (sólo el que vamos a utilizar) Con una respuesta de clave sense ilegal de un comando no soportado: #define ASC_INVALID_COMMAND_OPCODE 0x20 #define ASCQ_INVALID_COMMAND_OPCODE 0x00 Con una respuesta de clave sense ilegal para probar si la unidad está disponible: #define ASC_LOGICAL_UNIT_NOT_SUPPORTED 0x25 #define ASCQ_LOGICAL_UNIT_NOT_SUPPORTED 0x00 Con una clave sense Not ready #define ASC_LOGICAL_UNIT_DOES_NOT_RESPOND 0x05 #define ASCQ_LOGICAL_UNIT_DOES_NOT_RESPOND 0x00 #define ASC_MEDIUM_NOT_PRESENT 0x3a #define ASCQ_MEDIUM_NOT_PRESENT 0x00 #define ASC_LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE 0x04 #define ASCQ_LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE 0x00 23 Documento creado por Slalen para Electronics Strange World #define ASC_LOGICAL_UNIT_IN_PROCESS 0x04 #define ASCQ_LOGICAL_UNIT_IN_PROCESS 0x01 #define ASC_LOGICAL_UNIT_NOT_READY_INIT_REQD 0x04 #define ASCQ_LOGICAL_UNIT_NOT_READY_INIT_REQD 0x02 #define ASC_LOGICAL_UNIT_NOT_READY_INTERVENTION_REQD 0x04 #define ASCQ_LOGICAL_UNIT_NOT_READY_INTERVENTION_REQD 0x03 #define ASC_LOGICAL_UNIT_NOT_READY_FORMATTING 0x04 #define ASCQ_LOGICAL_UNIT_NOT_READY_FORMATTING 0x04 #define ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21 #define ASCQ_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x00 #define ASC_WRITE_PROTECTED 0x27 #define ASCQ_WRITE_PROTECTED 0x00 (bit) mMSDRxIsBusy(void) Esta macro se utiliza para comprobar si el Endpoint MSD OUT está ocupado (controlado por el SIE) o no. Uso típico: if(mMSDRxIsBusy()) #define mMSDRxIsBusy() MSD_BD_OUT.Stat.UOWN (bit) mMSDTxIsBusy(void) Esta macro se utiliza para comprobar si el Endpoint MSD IN está ocupado (controlado por el SIE) o no. Uso típico: if(mMSDTxIsBusy()) #define mMSDTxIsBusy() MSD_BD_IN.Stat.UOWN (bit) mMin(void) Esta macro se utiliza para encontrar el menor de dos argumentos. Uso típico: mMin(A, B) #define mMin(A,B) (A<B)?A:B 1.1.11.3. Estructuras typedef struct _USB_MSD_CBW { dword dCBWSignature; dword dCBWTag; con el 31 bytes totales CBW 55 53 42 43h Enviado por el host, el dispositivo se hace eco valor en CSW (asociado a CSW con CBW) 24 Librerías C18 del USB dword dCBWDataTransferLength; transferir byte bCBWFlags; bits 0 byte bCBWLUN; una byte bCBWCBLength; byte CBWCB[16]; } USB_MSD_CBW; typedef struct { escribir 10 Número de bytes de datos que el host espera Flags CBW, bit 7 = 0 salida de datos del hostdispositivo; bit 7=1 dispositivo-host, el resto de MS1bits son siempre cero, 0 en nuestro caso es sola unidad lógica MS3bits son cero Bloque de comando que ejecuta el dispositivo /Bloque de comando para leer 10 (0x28) y (0x2a) comandos byte Opcode; byte Flags; DWORD LBA; byte GroupNumber; WORD TransferLength; byte Control; } ReadWriteCB; b7-b5 lectura protegida, b4 DPO, b3 FUA, b2 Reservado, b1 FUA_NV, b0 Obsoleto b4-b0 es el número de grupo el resto reservados typedef struct { byte Opcode; byte EVPD; byte PageCode; word AllocationLength; byte Control; } InquiryCB; Formato del comando Inquiry typedef struct { byte Opcode; byte Reserved1; dword LBA; word Reserved2; byte PMI; byte Control; } ReadCapacityCB; capacidad de lectura 10 typedef struct { byte Opcode; byte Desc; word Reserved; byte AllocationLength; byte Control; } RequestSenseCB; Respuesta Sense 0x03 typedef struct { byte Opcode; Modo Sense 0x1A sólo b0 enable vital product data Bloque de dirección lógico Partial medium Indicator sólo b0 25 Documento creado por Slalen para Electronics Strange World byte DBD; byte PageCode; código Actualmente sólo se utiliza b3 como bloque descriptor desactivado b7,b6 PC=página de control, b5-b0 página de Página de Control bits 00=> valor actual, 01=>valores modificables,10=>valor por defecto, 11=>valores guardados byte SubPageCode; byte AllocationLength; byte Control; } ModeSenseCB; typedef struct { byte Opcode; byte Reserved[3]; byte Prevent; byte Control; } PreventAllowMediumRemovalCB; Prevenir el permiso de retirada del medio 0x1E typedef struct { byte Opcode; dword Reserved; byte Control; } TestUnitReadyCB; Unidad de prueba disponible 0x00 typedef struct { byte Opcode; byte VRProtect; Verificar 10 Comando 0x2F dword LBA; byte GroupNumber; word VerificationLength; byte Control; } VerifyCB; typedef struct { byte Opcode; byte Immed; word Reserved; byte Start; Sólo se previenen b1-b0, el resto reservados b7-b5 VRProtect, b4 DPO, b3-b2,Reservado, b1 BYTCHK, b0 Obsoleto Número del grupo b4-b0, el resto reservado STOP_START 0x1B b7-b4 Condición de energía, b3-b2 reservedo, b1 LOEJ, b0 Start byte Control; } StopStartCB; typedef struct _USB_MSD_CSW { dword dCSWSignature; dword dCSWTag; dword dCSWDataResidue; CSW 55 53 42 53h firma del paquete de CSW eco dCBWTag del paquete CBW diferencia en los datos esperados (dCBWDataTransferLength) y la cantidad actual procesada/enviada 26 Librerías C18 del USB byte bCSWStatus; 00h Comando aprobado, 01h Comando Fallido, 02h Error de fase, el resto obsoleto/reservado } USB_MSD_CSW; typedef struct { byte Peripheral; byte Removble; byte Version; byte Response_Data_Format; byte AdditionalLength; byte Sccstp; byte bqueetc; byte CmdQue; Clasificador del periférico:3; Periférico_DevType:5; medio removible bit7 = 0 medio no removible, resto reservado versión b7,b6 Obsoleto, b5 Acceso de control coordinado, b4 direccionamiento jerárquico soportado b3:0 respuesta al formato de datos 2 indica que la respuesta esta en el formato definido por spec longitud en bytes de los datos que quedan de la indagación estándar b7 SCCS, b6 ACC, b5-b4 TGPS, b3 3PC, b2-b1 Reservado, b0 Protegido b7 bque, b6- EncServ, b5-VS, b4-MultiP, b3-MChngr, b2-b1 Obsoleto, b0-Addr16 b7-b6 Obsoleto, b5-WBUS, b4-Sync, b3-Linked, b2 Obsoleto, b1 Cmdque, b0-VS char vendorID[8]; char productID[16]; char productRev[4]; } InquiryResponse; typedef struct { byte ModeDataLen; byte MediumType; unsigned Resv:4; unsigned DPOFUA:1; unsigned notused:2; unsigned WP:1; byte BlockDscLen; } tModeParamHdr; 0 indica que no soporta los bits DPO y FUA 0 indica que no protege la escritura Longitud del Bloque Descriptor Modo corto del bloque descriptor LBA (ver Página 1009, SBC-2) typedef struct { byte NumBlocks[4]; byte Resv; byte BlockLen[3]; } tBlockDescriptor; reservado /* Page_0 mode page format */ typedef struct { unsigned PageCode:6; unsigned SPF:1; SPC-3 7.4.5 SubPageFormat=0 medio Page_0 formato 27 Documento creado por Slalen para Electronics Strange World unsigned PS:1; byte PageLength; byte ModeParam[]; } tModePage; Parámetros salvables si 2..n bytes del modo parámetro PageLength=n-1 modo parámetros typedef struct { tModeParamHdr Header; tBlockDescriptor BlockDsc; tModePage modePage; } ModeSenseResponse; Formato fijado si el bit Desc de la respuesta sense CBW es 0 typedef union { struct { byte _byte[18]; }; struct { unsigned ResponseCode:7; unsigned VALID:1; byte Obsolete; unsigned SenseKey:4; unsigned Resv:1; unsigned ILI:1; unsigned EOM:1; unsigned FILEMARK:1; DWORD Information; byte AddSenseLen; DWORD CmdSpecificInfo; byte ASC; byte ASCQ; byte FRUC; byte SenseKeySpecific[3]; b6-b0 es el código de respuesta fijado o el formato del descriptor Poner a 1 indica que el campo de información tiene un valor válido Referencia SPC-3 Sección 4.5.6 Indicador de la longitud incorrecta Fin del medio para los comandos READ y SPACE Tipo de dispositivo o comando específico (SPC-33.1.18) Número de bytes sense adicionales que siguen <=244 depende del comando de la excepción ocurrida código sense adicional código sense adicional sección clasificada 4.5.2.1 SPC-3 SKSV son los msb de la clave sense específica de campo válido fijado=>SKS válido. Los 18-n bytes sense adicionales se pueden definir más tarde 18bytes de respuesta sense de formato fijado }; } RequestSenseResponse; 28 Librerías C18 del USB 1.1.11.4. Externas extern CSD gblCSDReg; 1.1.11.5. declarado en sdcard.c Prototipos públicos void USBCheckMSDRequest(void); void ProcessIO(void); void SDCardInit(void); void MSDInitEP(void); 29 Documento creado por Slalen para Electronics Strange World 1.1.12. CDC.H: DISPOSITIVOS DE COMUNICACIÓN Esta es la librería de la clase CDC. Se definen las transferencias creando funciones para realizarlas. No hay que modificarla. 1.1.12.1. Incluye #include "system\typedefs.h" 1.1.12.2. Definiciones Repuesta de clase específica: #define SEND_ENCAPSULATED_COMMAND #define GET_ENCAPSULATED_RESPONSE #define SET_COMM_FEATURE #define GET_COMM_FEATURE #define CLEAR_COMM_FEATURE #define SET_LINE_CODING #define GET_LINE_CODING #define SET_CONTROL_LINE_STATE #define SEND_BREAK 0x00 0x01 0x02 0x03 0x04 0x20 0x21 0x22 0x23 Notificaciones Nota: las notificaciones se obtienen de la interface de comunicación (Endpoint Interrupción) #define NETWORK_CONNECTION #define RESPONSE_AVAILABLE #define SERIAL_STATE 0x00 0x01 0x20 Código de la clase del dispositivo: #define CDC_DEVICE 0x02 Código de la clase de la interfaz de comunicación #define COMM_INTF 0x02 Código de la subclase de la interfaz de comunicación #define ABSTRACT_CONTROL_MODEL 0x02 Código del protocolo de control de la clase de la interfaz de comunicación #define V25TER 0x01 Comandos comunes AT ("Hayes(TM)") Código de la clase de la interfaz de datos #define DATA_INTF 0x0A Código del protocolo de la clase de la interfaz de datos #define NO_PROTOCOL 0x00 No necesita un protrocolo de clase específico Código selector de las características de la comunicación #define ABSTRACT_STATE 0x01 30 Librerías C18 del USB #define COUNTRY_SETTING 0x02 Descriptores funcionales Tipos de valor del campo bDscType #define CS_INTERFACE #define CS_ENDPOINT 0x24 0x25 bDscSubType en descriptores funcionales #define DSC_FN_HEADER #define DSC_FN_CALL_MGT #define DSC_FN_ACM 0x00 0x01 0x02 #define DSC_FN_DLM #define DSC_FN_TELEPHONE_RINGER #define DSC_FN_RPT_CAPABILITIES #define DSC_FN_UNION #define DSC_FN_COUNTRY_SELECTION #define DSC_FN_TEL_OP_MODES #define DSC_FN_USB_TERMINAL 0x03 0x04 0x05 0x06 0x07 0x08 0x09 ACM – Administración de control abstracta DLM – Dirección de línea directa Estados de tranferencia CDC Bulk IN #define CDC_TX_READY #define CDC_TX_BUSY #define CDC_TX_BUSY_ZLP #define CDC_TX_COMPLETING 0 1 2 3 ZLP: Paquete de longitud cero BOOL mUSBUSARTIsTxTrfReady(void) Esta macro se utiliza para comprobar si la clase CDC está disponible para enviar mas datos. Uso típico: if(mUSBUSARTIsTxTrfReady()) #define mUSBUSARTIsTxTrfReady() (cdc_trf_state == CDC_TX_READY) (bit) mCDCUsartRxIsBusy(void) Esta macro se utiliza para comprobar si el Endpoint CDC Bulk OUT está ocupado (controlado por el SIE) o no. Uso típico: if(mCDCUsartRxIsBusy()) #define mCDCUsartRxIsBusy() CDC_BULK_BD_OUT.Stat.UOWN (bit) mCDCUsartTxIsBusy(void) Esta macro se utiliza para comprobar si el Endpoint CDC Bulk IN está ocupado (controlado por el SIE) o no. 31 Documento creado por Slalen para Electronics Strange World Uso típico: if(mCDCUsartTxIsBusy()) #define mCDCUsartTxIsBusy() CDC_BULK_BD_IN.Stat.UOWN byte mCDCGetRxLength(void) Salida: devuelve cdc_rx_len mCDCGetRxLength se utiliza para recuperar el número de bytes que se han copiado al buffer del usuario en la última llamada a la función getsUSBUSART. #define mCDCGetRxLength() cdc_rx_len void mUSBUSARTTxRam(byte *pData, byte len) Precondición: cdc_trf_state tiene que estar en el estado CDC_TX_READY. El valor de ‘len’ tiene que se igual o menor de 255bytes. Entrada: pDdata: Puntero al comienzo de la localización de los bytes de datos. len: número de bytes que se van a transferir. Esta macro se utiliza para transferir datos localizados en la memoria de datos. Utilizar esta macro cuando: 1. La longitud de la transferencia se conoce 2. Los datos no terminan con uno nulo Nota: Esta macro sólo manipula la transferencia setup. La transferencia actual la manipula CDCTxService(). #define mUSBUSARTTxRam(pData,len) { pCDCSrc.bRam = pData; cdc_tx_len = len; cdc_mem_type = _RAM; cdc_trf_state = CDC_TX_BUSY; } void mUSBUSARTTxRom(rom byte *pData, byte len) Precondición: cdc_trf_state tiene que estar en el estado CDC_TX_READY. El valor de ‘len’ tiene que se igual o menor de 255bytes. Entrada: pDdata: Puntero al comienzo de la localización de los bytes de datos. len: número de bytes que se van a transferir. Esta macro se utiliza para transferir datos localizados en la memoria de programa. Utilizar esta macro cuando: 3. La longitud de la transferencia se conoce 4. Los datos no terminan con uno nulo Nota: Esta macro sólo manipula la transferencia setup. La transferencia actual la manipula CDCTxService(). 32 Librerías C18 del USB #define mUSBUSARTTxRom(pData,len) { pCDCSrc.bRom = pData; cdc_tx_len = len; cdc_mem_type = _ROM; cdc_trf_state = CDC_TX_BUSY; } 1.1.12.3. Estructuras Estructura de la línea de codificación #define LINE_CODING_LENGTH typedef union _LINE_CODING { struct { byte _byte[LINE_CODING_LENGTH]; }; struct { DWORD byte byte byte }; } LINE_CODING; 0x07 dwDTERate; Estructura de datos compleja bCharFormat; bParityType; bDataBits; typedef union _CONTROL_SIGNAL_BITMAP { byte _byte; struct { unsigned DTE_PRESENT; [0] No Presente [1] Presente unsigned CARRIER_CONTROL; [0] Desactiva [1] Activa }; } CONTROL_SIGNAL_BITMAP; Descriptor de cabecera funcional typedef struct _USB_CDC_HEADER_FN_DSC { byte bFNLength; byte bDscType; byte bDscSubType; word bcdCDC; } USB_CDC_HEADER_FN_DSC; Descriptor funcional de dirección de control abstracto typedef struct _USB_CDC_ACM_FN_DSC 33 Documento creado por Slalen para Electronics Strange World { byte bFNLength; byte bDscType; byte bDscSubType; byte bmCapabilities; } USB_CDC_ACM_FN_DSC; Descriptor funcional de unión typedef struct _USB_CDC_UNION_FN_DSC { byte bFNLength; byte bDscType; byte bDscSubType; byte bMasterIntf; byte bSaveIntf0; } USB_CDC_UNION_FN_DSC; Descriptor funcional de control de llamadas typedef struct _USB_CDC_CALL_MGT_FN_DSC { byte bFNLength; byte bDscType; byte bDscSubType; byte bmCapabilities; byte bDataInterface; } USB_CDC_CALL_MGT_FN_DSC; 1.1.12.4. Externas extern byte cdc_rx_len; extern byte cdc_trf_state; extern POINTER pCDCSrc; extern byte cdc_tx_len; extern byte cdc_mem_type; 1.1.12.5. Prototipos publicos void USBCheckCDCRequest(void); void CDCInitEP(void); byte getsUSBUSART(char *buffer, byte len); void putrsUSBUSART(const rom char *data); void putsUSBUSART(char *data); void CDCTxService(void); #endif CDC_H 34 Librerías C18 del USB 1.1.13. HID.H: DISPOSITIVO INTERFAZ CON HUMANOS Esta es la librería de la clase HID. Se definen las transferencias creando funciones para realizarlas. No hay que modificarla. 1.1.13.1. Incluye #include "system\typedefs.h" 1.1.13.2. Definiciones Repuestas de clase específicas: #define GET_REPORT #define GET_IDLE #define GET_PROTOCOL #define SET_REPORT #define SET_IDLE #define SET_PROTOCOL 0x01 0x02 0x03 0x09 0x0A 0x0B Obtener informe Obtener reposo Obtener protocolo Fijar informe Fijar reposo Fijar protocolo Tipos de clase de descriptor: #define DSC_HID #define DSC_RPT #define DSC_PHY 0x21 0x22 0x23 Descriptor HID Descriptor informe 0x00 0x01 Protocolo de inicio Informe de protocolo Selección de protocolo: #define BOOT_PROTOCOL #define RPT_PROTOCOL Código de clase de interfaz HID: #define HID_INTF 0x03 Interfaz HID Código de subclase de interfaz HID: #define BOOT_INTF_SUBCLASS 0x01 Código de protocolo de clase de interfaz: #define HID_PROTOCOL_NONE #define HID_PROTOCOL_KEYBOAD #define HID_PROTOCOL_MOUSE 0x00 0x01 0x02 Ninguno Teclado Ratón (bit) mHIDRxIsBusy() Esta macro comprueba si el Endpoint de salida del HID está ocupado (controlado por el SIE) o no. Aplicación típica: if(mHIDRxIsBusy()) 35 Documento creado por Slalen para Electronics Strange World #define mHIDRxIsBusy() HID_BD_OUT.Stat.UOWN (bit) mHIDTxIsBusy() Esta macro comprueba si el Endpoint de entrada del HID está coupado (controlado por el SIE) o no. Aplicación típica: if(mHIDTxIsBusy()) #define mHIDTxIsBusy() HID_BD_IN.Stat.UOWN byte mHIDGetRptRxLength() Salida: mHIDGetRptRxLength devuelve un informe de la longitud del receptor HID (hid_rpt_rx_len). La mHIDGetRptRxLength se utiliza para recuperar el número de bytes copiándolos al buffer de usuario en orden de la última llamada a la función HIDRxReport. #define mHIDGetRptRxLength() 1.1.13.3. hid_rpt_rx_len Estructuras typedef struct _USB_HID_DSC_HEADER Cabecera del descriptor HID { byte bDscType; Tipo de descriptor word wDscLength; Longitud del descriptor } USB_HID_DSC_HEADER; typedef struct _USB_HID_DSC Descriptor HID { Longitud Tipo de descriptor Bcd del HID byte bLength; byte bDscType; word bcdHID; Código de territorio Número del descriptor byte bCountryCode; byte bNumDsc; USB_HID_DSC_HEADER hid_dsc_header[HID_NUM_OF_DSC]; HID_NUM_OF_DSC se define en autofiles\usbcfg.h } USB_HID_DSC; 1.1.13.4. Externas extern byte hid_rpt_rx_len; 1.1.13.5. Prototipos públicos void HIDInitEP(void); void USBCheckHIDRequest(void); void HIDTxReport(char *buffer, byte len); byte HIDRxReport(char *buffer, byte len); 1.1.14. IO_CFG.H En esta librería se definen los puertos utilizados en la aplicación USB. 36 Librerías C18 del USB Si no se desea incluir hay que definir el apartado del USB ya que si no, la compilación daría un error al no definir los puertos Bus Sense y Power Sense. 1.1.14.1. Incluye #include "autofiles\usbcfg.h" 1.1.14.2. Tris #define INPUT_PIN #define OUTPUT_PIN 1.1.14.3. 1 0 USB #define tris_usb_bus_sense TRISAbits.TRISA1 entrada #if defined(USE_USB_BUS_SENSE_IO) #define usb_bus_sense PORTAbits.RA1 #else #define usb_bus_sense 1 #endif #define tris_self_power TRISAbits.TRISA2 entrada #if defined(USE_SELF_POWER_SENSE_IO) #define self_power PORTAbits.RA2 #else #define self_power 1 #endif Interfaz del transmisor externo #define tris_usb_vpo #define tris_usb_vmo #define tris_usb_rcv #define tris_usb_vp #define tris_usb_vm #define tris_usb_oe TRISBbits.TRISB3 TRISBbits.TRISB2 TRISAbits.TRISA4 TRISCbits.TRISC5 TRISCbits.TRISC4 TRISCbits.TRISC1 Salida Salida Entrada Entrada Entrada Salida #define tris_usb_suspnd TRISAbits.TRISA3 Salida 1.1.14.4. LED #define mInitAllLEDs() #define mLED_1 #define mLED_2 #define mLED_3 #define mLED_4 #define mLED_1_On() #define mLED_2_On() #define mLED_3_On() #define mLED_4_On() #define mLED_1_Off() #define mLED_2_Off() LATD &= 0xF0; TRISD &= 0xF0; LATDbits.LATD0 LATDbits.LATD1 LATDbits.LATD2 LATDbits.LATD3 mLED_1 = 1; mLED_2 = 1; mLED_3 = 1; mLED_4 = 1; mLED_1 = 0; mLED_2 = 0; 37 Documento creado por Slalen para Electronics Strange World #define mLED_3_Off() #define mLED_4_Off() #define mLED_1_Toggle() #define mLED_2_Toggle() #define mLED_3_Toggle() #define mLED_4_Toggle() 1.1.14.5. Interruptores #define mInitAllSwitches() #define mInitSwitch2() #define mInitSwitch3() //#define sw2 #define sw3 1.1.14.6. mLED_3 = 0; mLED_4 = 0; mLED_1 = !mLED_1; mLED_2 = !mLED_2; mLED_3 = !mLED_3; mLED_4 = !mLED_4; TRISBbits.TRISB4=1;TRISBbits.TRISB5=1; TRISBbits.TRISB4=1; TRISBbits.TRISB5=1; PORTBbits.RB4 PORTBbits.RB5 Potenciómetro #define mInitPOT() TRISAbits.TRISA0=1;ADCON0=0x01;ADCON2=0x3C; 1.1.14.7. SPI: Líneas de Chip Select #define tris_cs_temp_sensor TRISBbits.TRISB2 #define cs_temp_sensor LATBbits.LATB2 #define tris_cs_sdmmc TRISBbits.TRISB3 #define cs_sdmmc LATBbits.LATB3 1.1.14.8. Salida Salida SDMMC #define TRIS_CARD_DETECT #define CARD_DETECT #define TRIS_WRITE_DETECT #define WRITE_DETECT TRISBbits.TRISB4 PORTBbits.RB4 TRISAbits.TRISA4 PORTAbits.RA4 38 Entrada Entrada Librerías C18 del USB 1.1.15. INTERRUPT.H En esta librería se incluye todo lo referente a las interrupciones. El usuario la modificará dependiendo de la aplicación. 1.1.15.1. Incluye #include "system\typedefs.h" 1.1.15.2. Definiciones #define mEnableInterrupt() INTCONbits.GIE = 1; 1.1.15.3. Prototipos void low_isr(void); void high_isr(void); 39 Documento creado por Slalen para Electronics Strange World 1.1.16. USB_COMPILE_TIME_VALIDATION.H: VALIDACIÓN DEL TIEMPO DE COMPILADO Esta librería se utiliza para comprobar errores en la compilación. Es totalmente opcional. 1.1.16.1. Incluye #include "system\typedefs.h" #include "system\usb\usb.h" 1.1.16.2. Validación del USB #if (EP0_BUFF_SIZE != 8) && (EP0_BUFF_SIZE != 16) && (EP0_BUFF_SIZE != 32) && (EP0_BUFF_SIZE != 64) #error(Tamaño del buffer del Endpoint 0 incorrecto, comprueba "autofiles\usbcfg.h") #endif #if defined(HID_INT_OUT_EP_SIZE) #if (HID_INT_OUT_EP_SIZE > 64) #error(El tamaño del Endpoint de salida de HID no puede ser mayor de 64, comprueba "autofiles\usbcfg.h") #endif #endif #ifdef HID_INT_IN_EP_SIZE #if (HID_INT_IN_EP_SIZE > 64) #error(El tamaño del Endpoint de entrada de HID no puede ser mayor de 64, comprueba "autofiles\usbcfg.h") #endif #endif 1.2. LIBRERÍAS DEL MICROPROCESADOR Al instalar el compilador MPLAB C18 se crea una carpeta llamada lkr, en la cual hay unos archivos que se pueden añadir al proyecto. Estos archivos definen todos los puertos, registros, bits… del microprocesador que utilicemos en el proyecto. Es recomendable añadir este archivo ya que todas las librerías trabajan con las definiciones hechas en él. 40 Librerías C18 del USB 2. MACROS DEL COMPILADOR C18 Una macro es un conjunto de funciones declaradas en un programa. Se declaran por ser funciones muy utilizadas para ahorrar tiempo al programar. Para poder trabajar con las funciones definidas en las macros, hay que definir las librerías. En éstas hay un apartado llamado prototipos públicos en los que se declara la macro como función. Las macros que no aparecen en el apartado de prototipos públicos, son funciones internas que no se pueden llamar desde otro archivo. En este punto se comentan las macros de Microchip relativas al puerto USB. Como en las librerías, aparecen en rojo los datos que hay que modificar en cada aplicación, en azul los referentes a la clase y en verde todo el código fuente. 2.1. USBDRV.C: DRIVERS USB En este archive el fabricante ha creado el código relativo a la comunicación inicial. En el cual se declaran los estados del USB, los comprueba y determina en cual está la aplicación. 2.1.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #include "io_cfg.h" 2.1.2. Se necesita para USBCheckBusStatus() VARIABLES #pragma udata 2.1.3. PROTOTIPOS PRIVADOS void USBModuleEnable(void); void USBModuleDisable(void); void USBSuspend(void); void USBWakeFromSuspend(void); void USBProtocolResetHandler(void); void USB_SOF_Handler(void); void USBStallHandler(void); void USBErrorHandler(void); 2.1.4. DECLARACIONES #pragma code 2.1.4.1. void USBCheckBusStatus(void) Esta rutina activa/desactiva el módulo USB comprobado la señal de energía. 41 Documento creado por Slalen para Electronics Strange World void USBCheckBusStatus(void) { Detección de la conexión/desconexión del bus usb_bus_sense es un puerto de E/S definido en io_cfg.h #define USB_BUS_ATTACHED #define USB_BUS_DETACHED 1 0 if(usb_bus_sense == USB_BUS_ATTACHED) { if(UCONbits.USBEN == 0) USBModuleEnable(); } else { if(UCONbits.USBEN == 1) USBModuleDisable(); } end if(usb_bus_sense...) ¿Está el USB conectado? ¿Está el módulo apagado? Está apagado, actívalo ¿Está el módulo encendido? Está encendido, desactívalo Después de activar el modulo USB, hay un retardo para que la tensión de las líneas D+ o D- se pongan en alto lo suficiente para que salga de la condición SE0. La interrupción de Reset del USB tiene que estar enmascarada hasta que la condición SE0 esté borrada. Esto ayuda a prevenir que el firmware confunda este evento con un reset del host. if(usb_device_state == ATTACHED_STATE) { if(!UCONbits.SE0) { UIR = 0; UIE = 0; USB UIEbits.URSTIE = 1; UIEbits.IDLEIE = 1; usb_device_state = POWERED_STATE; } end if borrado } end if(usb_device_state == ATTACHED_STATE) Borra todas las interrupciones del USB Enmascara todas las interrupciones del Desenmascara la interrupción RESET Desenmascara la interrupción IDLE Lo demás espera hasta que SE0 esté } end USBCheckBusStatus 2.1.4.2. void USBModuleEnable(void) Esta rutina activa el módulo USB. Sólo se puede llamar esta rutina desde USBCheckBusStatus(). void USBModuleEnable(void) { UCON = 0; 42 Librerías C18 del USB UIE = 0; UCONbits.USBEN = 1; usb_device_state = ATTACHED_STATE; } end USBModuleEnable 2.1.4.3. Enmascara todas las interrupciones USB Activa el modulo y une el bus Definido en usbmmap.c y .h void USBModuleDisable(void) Esta rutina desactiva el modulo USB. Sólo se puede llamar esta rutina desde USBCheckBusStatus(). void USBModuleDisable(void) { UCON = 0; bus UIE = 0; usb_device_state = DETACHED_STATE; } end USBModuleDisable 2.1.4.4. Desactiva el modulo y desconecta del Enmascara todas las interrupciones USB Definido en usbmmap.c y .h void USBSoftDetach(void) Efectos secundarios: El dispositivo se tiene que reenumerar. USBSoftDetach desconecta eléctricamente el dispositivo del bus. Se utiliza para dejar de suministrar tensión VUSB a las resistencias pull-up. Las resistencias pull-down en el lado del host pondrán las señales diferenciales en bajo y el host registrará el evento como una desconexión. Como el cable USB no se desconecta físicamente, la energía suministrada a través de él la puede detectar el dispositivo. Cuando se llame la función USBCheckBusStatus(), se reconectará el dispositivo al bus. void USBSoftDetach(void) { USBModuleDisable(); } end USBSoftDetach 2.1.4.5. void USBDriverService(void) Esta rutina es el corazón del firmware. Controla las interrupciones USB. Nota: Las transiciones de estado del dispositivo son las siguientes: Desconectado->Conectado->Alimentado->Por defecto->Pendiente de dirección> ->Direccionado->Configurado->Listo void USBDriverService(void) { Punto para continuar dando servicio si el cable del USB no está unido. if(usb_device_state == DETACHED_STATE) return; Tarea A: Servicio de la interrupción de actividad if(UIRbits.ACTVIF && UIEbits.ACTVIE) USBWakeFromSuspend(); 43 Documento creado por Slalen para Electronics Strange World Punto para continuar dando servicio si el dispositivo está en modo suspendido. if(UCONbits.SUSPND==1) return; Tarea B: Servicio de la Interrupción Reset del Bus. Cuando se recibe un reset del bus durante el modo suspendido, lo primero hay que activar ACTVIF, una vez que UCONbits.SUSPND esté borrado, entonces el bit URSTIF se reafirmará. Esto es porque URSTIF se chequea después de ACTVIF. El flag de reset USB se enmascara cuando el USB está en estado desconectado o conectado, por lo que no se puede provocar un estado de reset del USB en estos dos estados. if(UIRbits.URSTIF && UIEbits.URSTIE) USBProtocolResetHandler(); Tarea C: Servicio de otras interrupciones. if(UIRbits.IDLEIF && UIEbits.IDLEIE) USBSuspend(); if(UIRbits.SOFIF && UIEbits.SOFIE) USB_SOF_Handler(); if(UIRbits.STALLIF && UIEbits.STALLIE) USBStallHandler(); if(UIRbits.UERRIF && UIEbits.UERRIE) USBErrorHandler(); Punto para continuar dando servicio si el bus no ha enviado un reset del bus. Al recibir el reset del bus, el dispositivo cambia al estado por defecto y está listo para comunicarse. if(usb_device_state < DEFAULT_STATE) return; Tarea D: Servicio de una interrupción de transacción completa if(UIRbits.TRNIF && UIEbits.TRNIE) { USBCtrlEPService sólo maneja las transacciones del EP0, ignora todas las transacciones de los otros EP. USBCtrlEPService(); Los demás Endpoint se pueden determinar después respondiendo a la clase del firmware. Cada driver del dispositivo sabe cuando una transacción OUT o IN está disponible comprobando el bit de propiedad del buffer. Un EP OUT lo debe controlar siempre el SIE hasta que el dato esté disponible. Un EP IN lo debe controlar siempre la CPU hasta que el dato esté disponible. Por esta lógica, no es necesario guardar el valor de USTAT de una transacción de un Endpoint distinto del 0 (non-EP0). UIRbits.TRNIF = 0; } end if(UIRbits.TRNIF && UIEbits.TRNIE) } end USBDriverService 2.1.4.6. void USBSuspend(void) void USBSuspend(void) { Nota: No borrar UIRbits.ACTVIF aquí Razón: ACTVIF sólo se genera si IDLEIF se ha generado. Es un ratio de generación de interrupción de 1:1. Por cada IDLEIF, habrá sólo un ACTVIF sea cual sea el número de transacciones en el bus. Si el ACTIF se borra aquí, puede ocurrir un problema cuando: [ IDLE ][bus activity -> <--- 3 ms -----> ^ ^ ACTVIF=1 44 Librerías C18 del USB IDLEIF=1 # # # # (#=Banderas de programa de interrogación) ^ Este ciclo de pregunta mira IDLEIF=1 y ACTVIF=1. Sin embargo, el programa sirve primero IDLEIF porque ACTIVIE=0. Si esta rutina borra el único ACTIVIF, puede que no salga nunca del modo suspendido. UIEbits.ACTVIE = 1; Activa las interrupciones del bus UIRbits.IDLEIF = 0; UCONbits.SUSPND = 1; Pone el modulo USB en el modo reserva de energía, el reloj SIE inactivo. En este punto el PIC puede ponerse en sleep, reposo, cambiar a un reloj más lento, etc. /* Sección modificable */ /* Final de la sección modificable */ } end USBSuspend 2.1.4.7. void USBWakeFromSuspend(void) void USBWakeFromSuspend(void) { Si se cambia la frecuencia de reloj, en este lugar se vuelve a poner la frecuencia original. UCONbits.SUSPND = 0; UIEbits.ACTVIE = 0; UIRbits.ACTVIF = 0; } end USBWakeFromSuspend 2.1.4.8. void USBRemoteWakeup(void) Esta función la tiene que llamar el usuario cuando el dispositivo se despierte por un estímulo externo que no sea ACTIVIF. Nota: La sección modificable en esta rutina se puede cambiar dependiendo de las especificaciones de la aplicación. La implementación actual bloquea temporalmente otras funciones de ejecución en un periodo de 1-13ms dependiendo de la frecuencia del núcleo. De acuerdo con las especificaciones del USB 2.0 en la sección 7.1.7.7, “El reinicio remoto del dispositivo tiene que bloquear la señal al menos por 1ms y no más de 15ms.” La idea aquí es utilizar un retraso por contador, usando un valor común que pueda trabajar con un gran rango de frecuencias del núcleo. Este valor es 1800. Ver la tabla de debajo: Frec del núcleo (MHz) 48 4 MIP 12 1 Periodo señal RESUME (ms) 1,05 12,6 Estos tiempos pueden ser distintos si se utiliza la optimización o el código de instrucciones entendido o cuando se tiene otra interrupción activa. Asegurarse usando del Stopwatch del MPLAB SIM. void USBRemoteWakeup(void) { 45 Documento creado por Slalen para Electronics Strange World static word delay_count; if(usb_stat.RemoteWakeup == 1) función { USBWakeFromSuspend(); UCONbits.RESUME = 1; Comprueba si el host a activado la reinicio remoto Reinicio del modo suspendido Comienzo de la señal RESUME /* Sección modificable */ /* Final de la sección modificable */ UCONbits.RESUME = 0; } endif } end USBRemoteWakeup 2.1.4.9. void USB_SOF_Handler(void) El host envía un paquete SOF a los dispositivos cada milisegundo. Esta interrupción puede ser útil en las pipes síncronas. Los diseñadores pueden implementar una rutina de realimentación como necesite. void USB_SOF_Handler(void) { /* Rutina de realimentación aquí */ UIRbits.SOFIF = 0; } end USB_SOF_Handler 2.1.4.10. void USBStallHandler(void) Precondición: El SIE tiene que haber mandado un paquete STALL al host. El STALLIF se active cada vez que el SIE envía un paquete STALL siempre que un Endpoint lo provoque. Una transacción Setup invalida la función STALL. Un Endpoint paralizado para el STALL cuando recibe un paquete setup. En este caso, el SIE aceptará el paquete Setup y activa la bandera TRNIF para informar el firmware. Una función STALL a un pipe Endpoint particular se desactivará automáticamente (dirección específica). Hay varios motivos para que un Endpoint se paralice: 1. Cuando se recibe una repuesta no soportada por el USB. Ejemplo: GET_DESCRIPTOR(DEVICE_QUALIFIER) 2. Cuando un Endpoint está actualmente parado 3. Cuando la clase del dispositivo especifica que Endpoint tiene que paralizarse en repuesta a un evento específico. 46 Librerías C18 del USB Ejemplo: Clase de dispositivo de almacenamiento masivo Si el CBW no es válido, el dispositivo parará la pipe Bulk de entrada. Nota: UEPn.EPSTALL tiene que escanear que Endpoint provoca el evento STALL. void USBStallHandler(void) { Todos los buffer descriptores del Endpoint 0 los controla el SIE, pero al recibir una transacción Setup, la CPU gobierna el EP0_OUT forzándolo por firmware. if(UEP0bits.EPSTALL == 1) { USBPrepareForNextSetupTrf(); UEP0bits.EPSTALL = 0; } UIRbits.STALLIF = 0; } end USBStallHandler 2.1.4.11. void USBErrorHandler(void) El propósito de esta interrupción es sólo por depuración durante el desarrollo. Chequea UEIR para ver error ha causado la interrupción. void USBErrorHandler(void) { UIRbits.UERRIF = 0; } end USBErrorHandler 2.1.4.12. void USBProtocolResetHandler(void) Precondición: Se tiene que haber recibido un reset del bus USB desde el host. Efectos secundarios: Esta rutina purga cualquier transacción pendiente. Borra la FIFO USTAT. Hay que llamar esta rutina cuando el reset del bus USB se ha recibido. Resetea la dirección del dispositivo a cero, desactiva todos los Endpoints menos el cero, inicializa el EP0 para que esté disponible las comunicaciones por defecto, borra todas los flags de interrupción, desenmascara las interrupciones USB aplicables y reinicializa las variables internas de estado-máquina. void USBProtocolResetHandler(void) { UEIR = 0; Borra todas los flags de error del USB UIR = 0; Borra todas las interrupciones USB UEIE = 0b10011111; Desenmascara todos los errores de interrupción USB UIE = 0b01111011; Activa todas las interrupciones menos ACTVIE UADDR = 0x00; Resetea a la dirección por defecto mDisableEP1to15(); Resetea todos los registros UEPn non-EP0 UEP0 = EP_CTRL|HSHK_EN; Inicializa el EP0 como EP Ctrl, ver usbdrv.h while(UIRbits.TRNIF == 1) Borra cualquier transacción pendiente UIRbits.TRNIF = 0; UCONbits.PKTDIS = 0; Se asegura de que el procesamiento de paquetes esté activo 47 Documento creado por Slalen para Electronics Strange World USBPrepareForNextSetupTrf(); Declarado en usbctrltrf.c usb_stat.RemoteWakeup = 0; Desactiva el flag de estado por defecto usb_active_cfg = 0; Borra la configuración activa usb_device_state = DEFAULT_STATE; } end USBProtocolResetHandler 2.1.5. FUNCIÓN AUXILIAR void ClearArray(byte* startAdr,byte count) { *startAdr; while(count) { _asm clrf POSTINC0,0 _endasm count--; } end while } end ClearArray 2.2. USB9.C Estas macros establecen la conexión. 2.2.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #include "io_cfg.h" 2.2.2. Requerido para el estado auto-alimentado VARIABLES #pragma udata 2.2.3. PROTOTIPOS PRIVADOS void USBStdGetDscHandler(void); void USBStdSetCfgHandler(void); void USBStdGetStatusHandler(void); void USBStdFeatureReqHandler(void); 2.2.4. DECLARACIONES #pragma code 48 Librerías C18 del USB 2.2.4.1. void USBCheckStdRequest(void) Esta rutina chequea el paquete de datos setup para ver si sabe cuando conectarse. void USBCheckStdRequest(void) { if(SetupPkt.RequestType != STANDARD) return; switch(SetupPkt.bRequest) { case SET_ADR: ctrl_trf_session_owner = MUID_USB9; usb_device_state = ADR_PENDING_STATE; Actualización de estado Ver USBCtrlTrfInHandler() en usbctrltrf.c para el próximo paso break; case GET_DSC: ctrl_trf_session_owner = MUID_USB9; if(SetupPkt.bDscType == DSC_DEV) { pSrc.bRom = (rom byte*)&device_dsc; wCount._word = sizeof(device_dsc); Activa la cuenta de datos } else if(SetupPkt.bDscType == DSC_CFG) { pSrc.bRom = (rom byte*)&cfg01; wCount._word = sizeof(cfg01); Activa la cuenta de datos } else if(SetupPkt.bDscType == DSC_STR) { pSrc.bRom = (rom byte*)&sd000; wCount._word = sizeof(sd000); Activa la cuenta de datos } else Esto se necesita para parar la respuesta DEVICE_QUALIFIER ctrl_trf_session_owner = MUID_NULL; usb_stat.ctrl_trf_mem = _ROM; Fija el tipo de memoria break; case SET_CFG: USBStdSetCfgHandler(); break; case GET_CFG: ctrl_trf_session_owner = MUID_USB9; pSrc.bRam = (byte*)&usb_active_cfg; Fija la fuente usb_stat.ctrl_trf_mem = _RAM; Fija el tipo de memoria LSB(wCount) = 1; Activa la cuenta de datos break; case GET_STATUS: USBStdGetStatusHandler(); break; case CLR_FEATURE: case SET_FEATURE: USBStdFeatureReqHandler(); break; 49 Documento creado por Slalen para Electronics Strange World case GET_INTF: ctrl_trf_session_owner = MUID_USB9; pSrc.bRam = (byte*)&usb_alt_intf+SetupPkt.bIntfID; usb_stat.ctrl_trf_mem = _RAM; LSB(wCount) = 1; break; case SET_INTF: ctrl_trf_session_owner = MUID_USB9; usb_alt_intf[SetupPkt.bIntfID] = SetupPkt.bAltID; break; case SET_DSC: case SYNCH_FRAME: default: break; } end switch } end USBCheckStdRequest 2.2.4.2. Fija la fuente Fija el tipo de memoria Activa la cuenta de datos void USBStdGetDscHandler(void) Esta rutina une la respuesta estándar GET_DESCRIPTOR. Utiliza tablas dinámicamente buscando el tamaño del descriptor. Esta rutina no se debe modificar si las tablas usbdsc.c en están declaradas correctamente. void USBStdGetDscHandler(void) { if(SetupPkt.bmRequestType == 0x80) { switch(SetupPkt.bDscType) { case DSC_DEV: ctrl_trf_session_owner = MUID_USB9; pSrc.bRom = (rom byte*)&device_dsc; wCount._word = sizeof(device_dsc); Activa la cuenta de datos break; case DSC_CFG: ctrl_trf_session_owner = MUID_USB9; pSrc.bRom = *(USB_CD_Ptr+SetupPkt.bDscIndex); wCount._word = *(pSrc.wRom+1); Activa la cuenta de datos break; case DSC_STR: ctrl_trf_session_owner = MUID_USB9; pSrc.bRom = *(USB_SD_Ptr+SetupPkt.bDscIndex); wCount._word = *pSrc.bRom; Activa la cuenta de datos break; } end switch } } usb_stat.ctrl_trf_mem = _ROM; end if end USBStdGetDscHandler Fija el tipo de memoria 50 Librerías C18 del USB 2.2.4.3. void USBStdSetCfgHandler(void) Esta rutina primero desactiva todos los Endpoints borrando los registros UEP. Configura (inicializa) los Endpoints especificados en la sección modificable. void USBStdSetCfgHandler(void) { ctrl_trf_session_owner = MUID_USB9; mDisableEP1to15(); Ver usbdrv.h ClearArray((byte*)&usb_alt_intf,MAX_NUM_INT); usb_active_cfg = SetupPkt.bCfgValue; if(SetupPkt.bCfgValue == 0) usb_device_state = ADDRESS_STATE; else { usb_device_state = CONFIGURED_STATE; /* Sección modificable */ BootInitEP(); /* Final de la sección modificable */ } end if(SetupPkt.bcfgValue == 0) } end USBStdSetCfgHandler 2.2.4.4. void USBStdGetStatusHandler(void) Esta rutina une la respuesta estándar GET_STATUS. void USBStdGetStatusHandler(void) { CtrlTrfData._byte0 = 0; CtrlTrfData._byte1 = 0; Inicializa el contenido switch(SetupPkt.Recipient) { case RCPT_DEV: ctrl_trf_session_owner = MUID_USB9; _byte0: bit0: Estado auto-alimentado [0] Alimentado por el bus [1] auto-alimentado bit1: Reinicio remoto [0] Desactivado [1] Activado if(self_power == 1) Auto-alimentado definido en io_cfg.h CtrlTrfData._byte0|=0b000000001; Activa bit0 if(usb_stat.RemoteWakeup == 1) CtrlTrfData._byte0|=0b00000010; } usb_stat definido en usbmmap.c Activa bit1 break; case RCPT_INTF: ctrl_trf_session_owner = MUID_USB9; No hay datos a actualizar break; case RCPT_EP: ctrl_trf_session_owner = MUID_USB9; _byte0: bit0: Estado parado [0] No parado [1] Parado pDst.bRam = (byte*)&ep0Bo+(SetupPkt.EPNum*8)+(SetupPkt.EPDir*4); if(*pDst.bRam & _BSTALL) Usar _BSTALL como máscara de bit CtrlTrfData._byte0=0x01; Activa bit0 break; end switch 51 Documento creado por Slalen para Electronics Strange World if(ctrl_trf_session_owner == MUID_USB9) { pSrc.bRam = (byte*)&CtrlTrfData; usb_stat.ctrl_trf_mem = _RAM; LSB(wCount) = 2; } end if(...) } end USBStdGetStatusHandler 2.2.4.5. Fija fuente Fija el tipo de memoria Activa la cuenta de datos void USBStdFeatureReqHandler(void) Esta rutina une las repuestas estándar SET y CLEAR FEATURES. void USBStdFeatureReqHandler(void) { if((SetupPkt.bFeature==DEVICE_REMOTE_WAKEUP)&&(SetupPkt.Recipient==RCPT_DE V)) { ctrl_trf_session_owner = MUID_USB9; if(SetupPkt.bRequest == SET_FEATURE) usb_stat.RemoteWakeup = 1; else usb_stat.RemoteWakeup = 0; } end if if((SetupPkt.bFeature==ENDPOINT_HALT)&&(SetupPkt.Recipient==RCPT_EP)&& (SetupPkt.EPNum!=0)) { ctrl_trf_session_owner = MUID_USB9; Se tiene que calcular la dirección en este punto pDst.bRam = (byte*)&ep0Bo+(SetupPkt.EPNum*8)+(SetupPkt.EPDir*4); if(SetupPkt.bRequest == SET_FEATURE) *pDst.bRam = _USIE|_BSTALL; else { if(SetupPkt.EPDir == 1) // IN *pDst.bRam = _UCPU; else *pDst.bRam = _USIE|_DAT0|_DTSEN; } end if } end if } end USBStdFeatureReqHandler 52 Librerías C18 del USB 2.3. USBCTRLTRF.C Estas macros controlan las transferencias. 2.3.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" 2.3.2. VARIABLES #pragma udata byte ctrl_trf_state; byte ctrl_trf_session_owner; POINTER pSrc; POINTER pDst; WORD wCount; 2.3.3. Estado de la transferencia de control Controlador de la sesión de transferencia actual Puntero a la fuente de datos Puntero al destino de los datos Contador de datos PROTOTIPOS PRIVADOS void USBCtrlTrfSetupHandler(void); void USBCtrlTrfOutHandler(void); void USBCtrlTrfInHandler(void); 2.3.4. DECLARACIONES #pragma code 2.3.4.1. void USBCtrlEPService(void) Precondición: USTAT está cargada con una dirección de Endpoint válida. USBCtrlEPService chequea tres tipos de transacciones que conoce como tratarlas y lo hace: 1. EP0 SETUP 2. EP0 OUT 3. EP0 IN Ignora los demás tipos. void USBCtrlEPService(void) { if(USTAT == EP00_OUT) { if(ep0Bo.Stat.PID == SETUP_TOKEN) USBCtrlTrfSetupHandler(); else USBCtrlTrfOutHandler(); } else if(USTAT == EP00_IN) USBCtrlTrfInHandler(); } end USBCtrlEPService EP0 SETUP EP0 OUT EP0 IN 53 Documento creado por Slalen para Electronics Strange World 2.3.4.2. void USBCtrlTrfSetupHandler(void) Precondición: El buffer SetupPkt está cargado con un dato válido de Setup. Esta rutina es una tarea para despachar y tiene tres estados. 1. Inicializa la transferencia de control de estados máquina. 2. Llama cada vez al módulo que sabe como servir la respuesta Setup del host. Ejemplo de módulo: USB9, HID, CDC, MSD,… Se añade una nueva clase, la tabla ClassReqHandler en usbdsc.c se tiene que actualizar para llamar todas las clases de unión disponibles. 3. Una vez que el módulo se arriesga a chequear si el responsable de servir la respuesta, en el estado 3 chequea la dirección de la transferencia para determinar como preparar el EP0 para la transferencia de control. Nota: El firmware del USB de Microchip tiene tres estados distintos para el control de los estados máquina: 1. WAIT_SETUP 2. CTRL_TRF_TX 3. CTRL_TRF_RX Mirar en el manual del firmware como cambia de un estado a otro. Una transferencia de control se compone de varias transacciones USB. Cuando se transfieren datos con muchas transacciones, es importante guardar los datos fuente, datos destino y cuenta de datos. Estos tres parámetros se guardan en pSrc, pDst y wCount. Un flag se utiliza para ver si la fuente de los datos es de la RAM o de la ROM. 2.3.4.3. { void USBCtrlTrfSetupHandler(void) byte i; Estado 1 ctrl_trf_state = WAIT_SETUP; ctrl_trf_session_owner = MUID_NULL; wCount._word = 0; Fijar el dueño a NULL Estado 2 USBCheckStdRequest(); Ver system\usb9\usb9.c /* Sección Modificable */ Insertar otra respuesta de unión de clase de dispositivo USB aquí /* Fin de la sección modificable */ Estado 3 USBCtrlEPServiceComplete(); } end USBCtrlTrfSetupHandler 2.3.4.4. void USBCtrlTrfOutHandler(void) Esta rutina une una transacción OUT de acuerdo con el estado de la transferencia de control que está actualmente activa. 54 Librerías C18 del USB Nota: Si la transferencia de control es del host al dispositivo, hay que notificar el dueño de la sesión cada transacción OUT para dar servicio a los datos recibidos. void USBCtrlTrfOutHandler(void) { if(ctrl_trf_state == CTRL_TRF_RX) { USBCtrlTrfRxService(); No preocuparse de reescribir el bit _KEEP porque si está activo, TRNIF no se generará en primer lugar. if(ep0Bo.Stat.DTS == 0) ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN; } else CTRL_TRF_TX USBPrepareForNextSetupTrf(); } end USBCtrlTrfOutHandler 2.3.4.5. void USBCtrlTrfInHandler(void) Esta rutina une una transacción IN de acuerdo con el estado de la transferencia de control que está actualmente activa. Nota: Si se fija la dirección de respuesta no modificará la actual hasta que se complete la transferencia de control. El final de la transferencia de control para una repuesta de cambio de dirección es una transacción IN. Por lo tanto se necesita para servir esta situación cuando la condición es correcta. La macro mUSBCheckAdrPendingState se define en usb9.h y está diseñada para servir este evento. void USBCtrlTrfInHandler(void) { mUSBCheckAdrPendingState(); Se tiene que comprobar si está en ADR_PENDING_STATE if(ctrl_trf_state == CTRL_TRF_TX) { USBCtrlTrfTxService(); if(ep0Bi.Stat.DTS == 0) ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bi.Stat._byte = _USIE|_DAT0|_DTSEN; } else CTRL_TRF_RX USBPrepareForNextSetupTrf(); } end USBCtrlTrfInHandler 2.3.4.6. void USBCtrlTrfTxService(void) Precondición: pSrc, wCount, y usb_stat.ctrl_trf_mem están configurados correctamente. 55 Documento creado por Slalen para Electronics Strange World Hay que llamar esta rutina en dos casos. Uno desde USBCtrlEPServiceComplete() y otro desde USBCtrlTrfInHandler(). Hay que tener cuidado con el control de una transferencia sobre múltiples transacciones USB. Nota: Esta rutina trabaja con Endpoints síncronos mayores que 256bytes y aquí se muestra un ejemplo de cómo tratar BC9 y BC8. En realidad, un Endpoint de control no puede ser mayor de 64bytes. void USBCtrlTrfTxService(void) { byte byte_to_send; Primero, hay que calcular cuantos bytes de datos se envían. if(wCount._word < EP0_BUFF_SIZE) byte_to_send = wCount._word; else byte_to_send = EP0_BUFF_SIZE; ep0Bi.Cnt = byte_to_send; Resta el número de bytes enviados a los del total. wCount._word -= byte_to_send; pDst.bRam = (byte*)&CtrlTrfData; Fija el puntero destino while(byte_to_send) { if(usb_stat.ctrl_trf_mem == _ROM) { *pDst.bRam = *pSrc.bRom; pSrc.bRom++; } else { *pDst.bRam = *pSrc.bRam; pSrc.bRam++; }//end if else pDst.bRam++; byte_to_send--; } end while } end USBCtrlTrfTxService 2.3.4.7. void USBCtrlTrfRxService(void) Precondiciones: pDst y wCount tienen que estar configurados correctamente. pSrc siempre es &CtrlTrfData y usb_stat.ctrl_trf_mem es _RAM. wCount tiene que estar configurado como 0 al principio de cada transferencia de control. Esta rutina no está completada. Comprueba una nueva versión del firmware. void USBCtrlTrfRxService(void) { byte byte_to_read; byte_to_read = ep0Bo.Cnt; Acumula el número total de bytes leídos wCount._word = wCount._word + byte_to_read; pSrc.bRam = (byte*)&CtrlTrfData; 56 Librerías C18 del USB while(byte_to_read) { *pDst.bRam = *pSrc.bRam; pDst.bRam++; pSrc.bRam++; byte_to_read--; } end while(byte_to_read) } end USBCtrlTrfRxService 2.3.4.8. void USBCtrlEPServiceComplete(void) Esta rutina consigue que la tarea en servicio sea una repuesta setup. Esta tarea sirve para configurar los controles del Endpoint apropiadamente para una situación dada. Hay tres: a. No hay unión para la respuesta, in este caso hay que mandar un STALL. b. El host ha respondido con una lectura de transferencia de control, los Endpoints se necesitan para determinar el camino. c. El host ha respondido con una escritura de transferencia de control o no se necesita un estado de control de datos, los Endpoints se necesitan para determinar el camino. Se resume el procesamiento del paquete borrando el bit PKTDIS. void USBCtrlEPServiceComplete(void) { if(ctrl_trf_session_owner == MUID_NULL) { Si ninguno sabe cómo dar servicio a esta respuesta, entonces se para. * If no one knows how to service this request then stall. Tiene que preparar el EP0 para que reciba la siguiente transacción SETUP. ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)&SetupPkt; ep0Bo.Stat._byte = _USIE|_BSTALL; ep0Bi.Stat._byte = _USIE|_BSTALL; } else El modulo demanda el control de la sesión de transferencia. { if(SetupPkt.DataDir == DEV_TO_HOST) { if(SetupPkt.wLength < wCount._word) wCount._word = SetupPkt.wLength; USBCtrlTrfTxService(); ctrl_trf_state = CTRL_TRF_TX; Control de lectura: <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]> 1. Prepara EP OUT para responder a una terminación temprana NOTA: Si algo va mal durante la transferencia de control, puede que el host no envíe la última fase de estado. Cuando pasa esto, pueden ocurrir dos cosas dependiendo del host: a) El host envía un RESET 57 Documento creado por Slalen para Electronics Strange World b) El host puede mandar una nueva transacción SETUP sin enviar primero un RESET. Para que el caso b) comunique correctamente, el EP OUT tiene que configurarse para recibir una transacción OUT de longitud cero o una nueva transacción SETUP. Como la transacción SETUP necesita que el bit DTS sea DAT0, el estado de longitud cero necesita que el bit DTS sea DAT1, la comprobación del bit DTS por hardware tiene que desactivarse. En este caso el SIE puede aceptar cualquiera de las dos transacciones. Además, el byte Cnt se tiene que fijar para prepararse para el dato SETUP (8bytes o mas), y el buffer de dirección tiene que apuntar al SetupPkt. ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)&SetupPkt; ep0Bo.Stat._byte = _USIE; Nota: DTSEN es 0 2. Prepara el EP IN para una transferencia de datos, Cnt tiene que estar inicializado para ser el responsable de una respuesta de dueño. ep0Bi.ADR = (byte*)&CtrlTrfData; ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; } else (SetupPkt.DataDir = HOST_TO_DEV) { ctrl_trf_state = CTRL_TRF_RX; Control Escritura: <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]> 1. Prepara el EP IN para responder ante una finalización temprana. Es lo mismo que una respuesta a un paquete de longitud cero para una transferencia de control sin fase de datos. ep0Bi.Cnt = 0; ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; 2. Prepara el EP OUT para recibir datos. ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)&CtrlTrfData; ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN; } end if(SetupPkt.DataDir == DEV_TO_HOST) } end if(ctrl_trf_session_owner == MUID_NULL) El bit PKTDIS se activa cuando se recibe una transacción Setup. Borrar para resumir el procesamiento del paquete. UCONbits.PKTDIS = 0; } end USBCtrlEPServiceComplete 2.3.4.9. void USBPrepareForNextSetupTrf(void) La rutina fuerza al EP0 OUT que esté listo para una nueva transacción Setup, y fuerza a que la CPU controle el EP0 IN. void USBPrepareForNextSetupTrf(void) { ctrl_trf_state = WAIT_SETUP; ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)&SetupPkt; ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN; usbmmap.h ep0Bi.Stat._byte = _UCPU; } end USBPrepareForNextSetupTrf Ver usbctrltrf.h Definido en usbcfg.h Inicialización EP0 buff EP0 IN buffer inicialización 58 dsc, ver Librerías C18 del USB 2.4. USBDSC.C: DESCRIPTORES USB Este archivo contiene la información de los descriptores USB. Se utiliza junto al archivo usbdsc.h. Cuando se añade o remueve un descriptor del menú de configuración de los descriptores, ej. CFG01, el usuario debe cambiar la estructura del descriptor definida en el archivo usbdsc.h. La estructura se utiliza para calcular el tamaño del descriptor, ej. sizeof(CFG01). Una configuración típica de los descriptores consiste en: La mínima configuración del descriptor (USB_CFG_DSC) Uno o más descriptores de interfaz (USB_INTF_DSC) Uno o más descriptores de Endpoint (USB_EP_DSC) Nombres convenidos: Ê Tipo USB_CFG_DSC se nombra cdxx, donde xx es el número de configuración. Este número debería ser el mismo que el valor del índice actual de esta configuración. Ê Tipo USB_INTF_DSC se nombra i<yy>a<zz>, donde yy es el número de la interfaz y zz es el número de la interfaz alterna. Ê Tipo USB_EP_DSC se nombra ep<##><d>_i<yy>a<zz>, donde ## es el número del Endpoint y d es la dirección de transferencia. El nombre de la interfaz se tiene que listar como un sufijo para identificar que interfaz pertenece al Endpoint. Ejemplo: Si un dispositivo tiene una configuración, dos interfaces, la interfaz 0 tiene dos Endpoints (IN y OUT), y la interfaz 1 tiene un Endpoint (IN). Entonces la estructura en usbdsc.h tiene que ser: #define CFG01 rom struct { } USB_CFG_DSC cd01; USB_INTF_DSC i00a00; USB_EP_DSC ep01o_i00a00; USB_EP_DSC ep01i_i00a00; USB_INTF_DSC i01a00; USB_EP_DSC ep02i_i01a00; cfg01 Ver que la jerarquía de los descriptores sigue a las especificaciones de necesidad del USB. Todos los Endpoint que pertenecen a una interfaz se tienen que listar inmediatamente después que la interfaz. Rellenar los valores del descriptor en el archivo usbdsc.c: [Descriptor de Configuration (USB_CFG_DSC)] 59 Documento creado por Slalen para Electronics Strange World El atributo de configuración tiene que tener la definición _DEFAULT como mínimo. Se pueden añadir opciones adicionales al tributo _DEFAULT. Las opciones disponibles son _SELF y _RWU. Estas definiciones se encuentran en el archivo usbdefs_std_dsc.h. El _SELF dice al host del USB que este dispositivo es autoalimentado. El _RWU dice al host del USB que el dispositivo soporta el reinicios remoto. [Descriptor del Endpoint (USB_EP_DSC)] Suponer el siguiente ejemplo: sizeof(USB_EP_DSC),DSC_EP,_EP01_OUT,_BULK,64,0x00 Los dos primeros parámetros son auto-explicativos. Especifican la longitud del descriptor del Endpoint (7) y el tipo de descriptor. El siguiente parámetro identifica el Endpoint, las definiciones se encuentran en usbdefs_std_dsc.h y tienen la siguiente convención: _EP<##>_<dir> donde ## es el número del endpoint y dir es la dirección de la transferencia. dir tiene el valor de ‘OUT’ o ‘IN’. El siguiente parámetro especifica el tipo de Endpoint. Las opciones disponibles son _BULK, _INT, _ISO, y _CTRL. El _CTRL no se utiliza normalmente porque el Endpoint de transferencia de control por defecto no se define en los descriptores USB. Cuando se utiliza la opción _ISO, se pueden añadir opciones adicionales. Ejemplo: _ISO|_AD|_FE Esto describe el Endpoint como una pipe síncrona con los atributos adoptivo y realimentación. Ver usbdefs_std_dsc.h y las especificaciones del USB por más detalles. El siguiente parámetro define el tamaño del Endpoint. El último parámetro es el intervalo de muestreo. Añadir un String al USB Una matriz de string descriptor debe tener el siguiente formato: rom struct{byte bLength;byte bDscType;word string[size];}sdxxx={ sizeof(sdxxx),DSC_STR,<text>}; La estructura proporciona un medio al compilador de C para calcular la longitud del string descriptor sdxxx, donde xxx es el número de índice. Los dos primeros bytes del descriptor son su longitud y tipo. El resto <text> son strings de texto que tienen que estar en formato unicode. El formato unicode se obtiene declarando cada carácter como un tipo de letra. Todo el texto string se declara como una matriz de letras con el número de caracteres igual a <size>. <size> se tiene que contar manualmente y meter en las declaraciones de la matriz. Ejemplo: Si el string es “USB”, entonces el string descriptor debe ser: (Utilizando índice 02) rom struct{byte bLength;byte bDscType;word string[3];}sd002={ sizeof(sd002),DSC_STR,'U','S','B'}; 60 Librerías C18 del USB Un proyecto USB puede que tenga varios strings y el firmware soporta el control de múltiples strings como una búsqueda en tabla. La búsqueda en tabla se define: rom const unsigned char *rom USB_SD_Ptr[]={&sd000,&sd001,&sd002}; La declaración de arriba tiene 3 strings, sd000, sd001 y sd002. Los strings se pueden añadir o borrar. sd000 es un string descriptor especial, define el idioma, normalmente es inglés americano (US English (0x0409)). El índice del string debe ser igual que el índice de posición de la matriz USB_SD_Ptr, &sd000 tiene que estar en la posición USB_SD_Ptr[0], &sd001 tiene que estar en la posición USB_SD_Ptr[1] y así sucesivamente. La búsqueda en tabla USB_SD_Ptr la utiliza la función de unión string en usb9.c. El esquema de búsqueda en tabla también se aplica a la descriptor de configuración. Un dispositivo USB puede tener varios descriptores de configuraciones, ej. CFG01, CFG02, etc. Para añadir un descriptor de configuración, el usuario tiene que implementar una estructura similar a CFG01. El siguiente paso es añadir el nombre del descriptor de configuración, ej. cfg01, cfg02…; a la búsqueda en tabla USB_CD_Ptr. USB_CD_Ptr[0] es muy fácil poner el titular ya que la configuración 0 es el estado no configurado de acuerdo con las especificaciones del USB. Los tipos de descriptor específicos se definen en: system\usb\usbdefs\usbdefs_std_dsc.h La información de configuración se define en: autofiles\usbcfg.h 2.4.1. INCLUYE #include "system\typedefs.h" #include "system\usb\usb.h" 2.4.2. CONSTANTES #pragma romdata Descriptor del dispositivo rom USB_DEV_DSC device_dsc= { sizeof(USB_DEV_DSC), DSC_DEV, 0x0200, 0x00, 0x00, 0x00, EP0_BUFF_SIZE, 0x04D8, 0x0000, Tamaño del descriptor en bytes Descriptor tipo DEVICE Número de versión del USB en formato BCD Código de Clase Código Subclase Código Protocolo Tamaño de paquete máximo para el EP0, ver usbcfg.h ID Fabricante ID Producto 61 Documento creado por Slalen para Electronics Strange World }; 0x0001, 0x01, 0x02, 0x00, 0x01 Descriptor de configuración 1 CFG01={ Descriptor de configuración sizeof(USB_CFG_DSC), DSC_CFG, sizeof(cfg01), 1, 1, 0, _DEFAULT|_RWU, 50, Descriptor de la interfaz sizeof(USB_INTF_DSC), DSC_INTF, 0, 0, 1, HID_INTF, BOOT_INTF_SUBCLASS, HID_PROTOCOL_MOUSE, 0, Número de versión del dispositivo en formato BCD String índice de fabricante String índice del producto String índice del número de serie del dispositivo Número de configuraciones posible Tamaño del descriptor en bytes Tipo del descriptor CONFIGURACIÓN Longitud total de datos de esta configuración Número de interfaces en esta configuración Valor del índice de esta configuración String índice de configuración Atributos, ver usbdefs_std_dsc.h Consumo máximo de corriente (2X mA) Tamaño del descriptor en bytes Tipo de descriptor INTERFACE Número de Interface Número alterno de configuración Número de Endpoints en esta interfaz Código de la Clase Código de la Subclase Código del Protocolo String índice de interfaz Descriptor de Clase Específica HID sizeof(USB_HID_DSC), Tamaño del descriptor en bytes DSC_HID, Tipo del descriptor HID 0x0101, Número de versión específica HID en formato BCD 0x00, Código del país (0x00 en los no soportados) HID_NUM_OF_DSC, Número de la clase del descriptor, ver usbcfg.h DSC_RPT, Tipo del informe del descriptor sizeof(hid_rpt01), Tamaño del informe del descriptor Descriptor del Endpoint sizeof(USB_EP_DSC),DSC_EP,_EP01_IN,_INT,HID_INT_IN_EP_SIZE,0x0A }; rom struct{byte bLength;byte bDscType;word string[1];}sd000={ sizeof(sd000),DSC_STR,0x0409}; rom struct{byte bLength;byte bDscType;word string[25];}sd001={ sizeof(sd001),DSC_STR, 'M','i','c','r','o','c','h','i','p',' ', 'T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.'}; rom struct{byte bLength;byte bDscType;word string[22];}sd002={ sizeof(sd002),DSC_STR, 'M','o','u','s','e',' ','I','n',' ','a',' ', 62 Librerías C18 del USB 'C','i','r','c','l','e',' ','D','e','m','o'}; rom struct{byte report[HID_RPT01_SIZE];}hid_rpt01={ 0x05, 0x01, Página de uso (Escritorio genérico) 0x09, 0x02, Uso (Ratón) 0xA1, 0x01, Colección (Aplicación) 0x09, 0x01, Uso (Puntero) 0xA1, 0x00, Colección (Física) 0x05, 0x09, Página de uso (Botones) 0x19, 0x01, Uso mínimo (01) 0x29, 0x03, Uso máximo (03) 0x15, 0x00, Mínimo lógico (0) 0x25, 0x01, Máximo lógico (0) 0x95, 0x03, Cuenta del informe (3) 0x75, 0x01, Tamaño del informe (1) 0x81, 0x02, Entrada (Dato, Variable, Absoluto) 0x95, 0x01, Cuenta del informe (1) 0x75, 0x05, Tamaño del informe (5) 0x81, 0x01, Entrada (Constante); 5 bit de relleno 0x05, 0x01, Uso de página (Escritorio genérico) 0x09, 0x30, Uso (X) 0x09, 0x31, Uso (Y) 0x15, 0x81, Mínimo lógico (-127) 0x25, 0x7F, Máximo lógico (127) 0x75, 0x08, Tamaño del informe (8) 0x95, 0x02, Cuenta del informe (2) 0x81, 0x06, Entrada (Dato, Variable, Relativo) 0xC0, 0xC0}; Fin de la colección rom const unsigned char *rom USB_CD_Ptr[]={&cfg01,&cfg01}; rom const unsigned char *rom USB_SD_Ptr[]={&sd000,&sd001,&sd002}; rom pFunc ClassReqHandler[1]= { &USBCheckHIDRequest }; #pragma code 2.5. USBMMAP.C Este archivo es el que controla la memoria del USB, sirve para asignar la memoria en cada instante a los Endpoint. Utiliza los tiempos de compilación de usbcfg.h instantáneamente para los Endpoints y sus buffers. Cada Endpoint necesita fijar un registro del buffer descriptor (BDT). Un BDT tiene 4bytes de longitud y una memoria específica en la RAM para cada Endpoint. El BDT del EP0 OUT está entre las direcciones 0x400 a 0x403, el BDT del EP0 IN en 0x404 a0x407, el del EP1 OUT 0x408 a 0x40B y así sucesivamente. Estas localizaciones son correctas con el Buffer Ping-Pong en Modo 0. Estas localizaciones 63 Documento creado por Slalen para Electronics Strange World están conectadas en el chip. Al hacerlas instantáneas, ej. volatile far BDT ep0Bo, es para proporcionar al compilador de C un camino para direccionar cada variable directamente. Esto es muy importante porque cuando se puede acceder a un registro directamente, se ahorra tiempo de ejecución y se reduce el tamaño de programa. Los Endpoints se definen con el número de Endpoint y la dirección de transferencia. Para simplificar, usbmmap.c sólo utiliza el número del Endpoint del esquema de direccionamiento de los registros BDT. Con este método si MAX_EP_NUMBER es 1, tenemos cuatro BDTs instantáneamente: uno para EP0IN, otro para EP0OUT, que se tiene que inicializar instantáneamente para las transferencias de control por defecto, y otros dos para EP1IN y EP1OUT. El nombre convenido es ep<#>B<d> donde # es el número del Endpoint y d es la dirección de transferencia, que puede ser <i> o <o>. El control de la memoria USB utiliza MAX_EP_NUMBER, definido en usbcfg.h, para saber que Endpoints se necesitan instantáneamente. Representa el número máximo de Endpoints que se direccionan, no cuantos Endpoints se utilizan. Como los BDTs para los Endpoints tienen la dirección asignada en el Bank 4 en hardware, configurar este valor con un dato muy grande puede que se utilice inadecuadamente la RAM. Por ejemplo, en una aplicación se utiliza los EP0 y EP4, el MAX_EP_NUMBER es 4, y no 2. Los Endpoints del medio (EP1, EP2 y EP3) no se utilizan, y los 24bytes de memoria asociados se pierden. No tiene mucho sentido saltar Endpoints, pero la decisión final la tiene el usuario. El paso siguiente es asignar los BDTs instantáneos a las distintas funciones del USB. El firmware asume que cada función del USB sabe que Endpoint utiliza, ej. la transferencia de control por defecto sabe que utiliza el EP0IN y el EP0OUT. Una clase HID puede elegir que Endpoint utiliza, pero una vez elegido tiene que saber el número. La asignación de los Endpoints de las funciones del USB se gobiernan en usbcfg.h. Esto ayuda a prevenir errores de tener más de una función USB con el mismo Endpoint. La sección “Distribución de los Endpoints” en usbcfg.h proporciona un ejemplo de cómo distribuir los Endpoints del USB con funciones USB. Se puede cambiar la configuración en esta sección. No hay una forma correcta de configuración y los usuarios tienen que elegir el método más adecuado para la aplicación. Normalmente, un usuario mapeará lo siguiente para una función de interfaz: 1. 2. 3. 4. El ID de la interfaz USB Los registros de control de los Endpoint (UEPn) El registro BDT (ep<#>B<d>) El tamaño del Endpoint. Ejemplo: Suponer una clase de dispositivo “foo”, que utiliza un Endpoint de salida de 64bytes y un Endpoint de entrada de 64bytes, entonces: #define FOO_INTF_ID 0x00 #define FOO_UEP UEP1 #define FOO_BD_OUT ep1Bo #define FOO_BD_IN ep1Bi #define FOO_EP_SIZE 64 64 Librerías C18 del USB El mapeo anterior elige la clase “foo” para utilizarse con el Endpoint 1. El nombre es arbitrario y se puede elegir otro que no sea FOO_???????. Como idea abstracta, el código para la clase “foo” se tiene que usar en las definiciones abstractas de FOO_BD_OUT,FOO_BD_IN y o ep1Bo o ep1Bi. Ver que el tamaño del Endpoint definido en el archivo usbcfg.h se utiliza de nuevo en el archivo usbmmap.c. Esto muestra que los dos archivos están muy relacionados. El buffer del Endpoint para cada función USB se localiza en el área del puertodual RAM y como después se tiene que hacer instantáneos los BDTs. Un ejemplo de declaración es: volatile far unsigned char[FOO_EP_SIZE] data; La palabra ‘volatile’ dice al compilador que no funcione ningún código de optimización en esta variable porque el contenido lo tiene que modificar el hardware. La palabra ‘far’ dice que la variable no se localiza en el área de RAM Accesible (0x0000x05F). Para que la variable sea accesible globalmente con otros ficheros, se tiene que declarar en el archivo de cabecera usbmmap.h como una definición externa, como extern volatile far unsigned char[FOO_EP_SIZE] data; Conclusión: Las dependencias entre usbcfg y usbmmap se pueden mostrar como: usbcfg[MAX_EP_NUMBER] -> usbmmap usbmmap[ep<#>B<d>] -> usbcfg usbcfg[EP size] -> usbmmap usbcfg[abstract ep definitions] -> usb9/hid/cdc/etc class code usbmmap[endpoint buffer variable] -> usb9/hid/cdc/etc class code El mapeo proporciona una manera directa de direccionado de BDT y un buffer del Enpoint. Esta forma utiliza menos punteros, y se equipara con un código de programa más rápido y pequeño. 2.5.1. INCLUYE #include "system\typedefs.h" #include "system\usb\usb.h" 2.5.2. VARIABLES GLOBALES DEL USB #pragma udata byte usb_device_state; USB_DEVICE_STATUS usb_stat; byte usb_active_cfg; byte usb_alt_intf[MAX_NUM_INT]; 2.5.3. Estados del dispositivo: Desconectado, Conectado, ... Flags globales del USB Valor de la configuración actual Matriz para guardar los datos de la configuración actual alterna para cada ID interfaz LOCALIZACIONES DE VARIABLES FIJAS DEL USB #pragma udata usbram4=0x400 Ver, usb4: 0x400-0x4FF(256-byte) Sección A: Tabla del Buffer Descriptor - 0x400 - 0x4FF(max) 65 Documento creado por Slalen para Electronics Strange World - MAX_EP_NUMBER se define en autofiles\usbcfg.h - BDT data type se define en system\usb\usbmmap.h #if(0 <= MAX_EP_NUMBER) volatile far BDT ep0Bo; Endpoint #0 BD Out volatile far BDT ep0Bi; Endpoint #0 BD In #endif #if(1 <= MAX_EP_NUMBER) volatile far BDT ep1Bo; volatile far BDT ep1Bi; #endif Endpoint #1 BD Out Endpoint #1 BD In #if(2 <= MAX_EP_NUMBER) volatile far BDT ep2Bo; volatile far BDT ep2Bi; #endif Endpoint #2 BD Out Endpoint #2 BD In #if(3 <= MAX_EP_NUMBER) volatile far BDT ep3Bo; volatile far BDT ep3Bi; #endif Endpoint #3 BD Out Endpoint #3 BD In #if(4 <= MAX_EP_NUMBER) volatile far BDT ep4Bo; volatile far BDT ep4Bi; #endif Endpoint #4 BD Out Endpoint #4 BD In #if(5 <= MAX_EP_NUMBER) volatile far BDT ep5Bo; volatile far BDT ep5Bi; #endif Endpoint #5 BD Out Endpoint #5 BD In #if(6 <= MAX_EP_NUMBER) volatile far BDT ep6Bo; volatile far BDT ep6Bi; #endif Endpoint #6 BD Out Endpoint #6 BD In #if(7 <= MAX_EP_NUMBER) volatile far BDT ep7Bo; volatile far BDT ep7Bi; #endif Endpoint #7 BD Out Endpoint #7 BD In #if(8 <= MAX_EP_NUMBER) volatile far BDT ep8Bo; volatile far BDT ep8Bi; #endif Endpoint #8 BD Out Endpoint #8 BD In #if(9 <= MAX_EP_NUMBER) volatile far BDT ep9Bo; volatile far BDT ep9Bi; Endpoint #9 BD Out Endpoint #9 BD In 66 Librerías C18 del USB #endif #if(10 <= MAX_EP_NUMBER) volatile far BDT ep10Bo; volatile far BDT ep10Bi; #endif Endpoint #10 BD Out Endpoint #10 BD In #if(11 <= MAX_EP_NUMBER) volatile far BDT ep11Bo; volatile far BDT ep11Bi; #endif Endpoint #11 BD Out Endpoint #11 BD In #if(12 <= MAX_EP_NUMBER) volatile far BDT ep12Bo; volatile far BDT ep12Bi; #endif Endpoint #12 BD Out Endpoint #12 BD In #if(13 <= MAX_EP_NUMBER) volatile far BDT ep13Bo; volatile far BDT ep13Bi #endif Endpoint #13 BD Out Endpoint #13 BD In #if(14 <= MAX_EP_NUMBER) volatile far BDT ep14Bo; volatile far BDT ep14Bi; #endif Endpoint #14 BD Out Endpoint #14 BD In #if(15 <= MAX_EP_NUMBER) volatile far BDT ep15Bo; Endpoint #15 BD Out volatile far BDT ep15Bi; Endpoint #15 BD In #endif Sección B: Espacio del Buffer del EP0 - Dos areas definidas para el buffer: A. CTRL_TRF_SETUP - Tamaño = EP0_BUFF_SIZE definido en autofiles\usbcfg.h - La estructura de datos detallada permite el direccionamiento directo de bits y bytes. B. CTRL_TRF_DATA - Tamaño = EP0_BUFF_SIZE definido en autofiles\usbcfg.h - La estructura de datos detallada permite el direccionamiento directo de los 8 bytes primeros. - Los dos tipos se definen en system\usb\usbdefs\usbdefs_ep0_buff.h volatile far CTRL_TRF_SETUP SetupPkt; volatile far CTRL_TRF_DATA CtrlTrfData; Sección C: Buffer CDC #pragma udata usbram5a=0x500 //See Linker Script,usb5:0x500#if defined(USB_USE_CDC) volatile far unsigned char cdc_notice[CDC_INT_EP_SIZE]; volatile far unsigned char cdc_data_rx[CDC_BULK_OUT_EP_SIZE]; volatile far unsigned char cdc_data_tx[CDC_BULK_IN_EP_SIZE]; #endif #pragma udata 67 Documento creado por Slalen para Electronics Strange World 2.6. USBGEN.C: USB GENÉRICO En este archivo se han creado todas las funciones de transferencia de los datos que desee el usuario en la clase genérica. 2.6.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #ifdef USB_USE_GEN 2.6.2. VARIABLES #pragma udata byte usbgen_rx_len; 2.6.3. DECLARACIONES #pragma code 2.6.4. API DEL USUARIO 2.6.4.1. void USBGenInitEP(void) USBGenInitEP inicializa Endpoints genéricos, buffer de los descriptores, estados máquina internos y variables. Hay que llamarla después de que el host haya enviado una repuesta SET_CONFIGURATION. Ver USBStdSetCfgHandler() en usb9.c como ejemplo. void USBGenInitEP(void) { usbgen_rx_len = 0; USBGEN_UEP = EP_OUT_IN|HSHK_EN; Activa 2 pipes de datos No hay que iniciar Cnt para las pipes de entrada. Razón: El número de bytes enviados al host varía de una transacción a otra. Cnt tiene que ser igual al número exacto de bytes a transmitir en una transacción IN dada. Este número de bytes sólo se conoce una vez que los datos hayan sido enviados. USBGEN_BD_OUT.Cnt = sizeof(usbgen_out); Fija el tamaño del buffer USBGEN_BD_OUT.ADR = (byte*)&usbgen_out; Fija la dirección del buffer USBGEN_BD_OUT.Stat._byte = _USIE|_DAT0|_DTSEN; Fija el estado USBGEN_BD_IN.ADR = (byte*)&usbgen_in; USBGEN_BD_IN.Stat._byte = _UCPU|_DAT1; Fija la dirección del buffer Fija el estado del buffer } end USBGenInitEP 2.6.4.2. void USBGenWrite(byte *buffer, byte len) Precondición: mUSBGenTxIsBusy() tiene que devolver “falso”. 68 Librerías C18 del USB El valor de ‘len’ tiene que ser igual o menor que USBGEN_EP_SIZE. Para un Endpoint interrupción/bulk, el tamaño máximo del buffer es de 64bytes. Entrada: buffer: Puntero a la localización de inicio de los bytes de datos. len: Número de bytes a transmitir. Esta macro se utiliza para transferir datos de la memoria de datos. Aplicación típica: if(!mUSBGenTxIsBusy()) USBGenWrite(buffer, 3); void USBGenWrite(byte *buffer, byte len) { byte i; El valor de ‘len’ tiene que ser igual o menor que USBGEN_EP_SIZE. Esta comprobación fuerza que se cumpla la precondición. if(len > USBGEN_EP_SIZE) len = USBGEN_EP_SIZE; Copia de los datos del buffer del usuario al buffer de la ram-dual. for (i = 0; i < len; i++) usbgen_in[i] = buffer[i]; USBGEN_BD_IN.Cnt = len; mUSBBufferReady(USBGEN_BD_IN); } end USBGenWrite 2.6.4.3. byte USBGenRead(byte *buffer, byte len) Precondición: El valor del argumento de entrada ‘len’ tiene que ser menor que el tamaño máximo del Endpoint responsable de la recepción del informe de datos del host para la clase HID. El argumento de entrada ‘buffer’ debe apuntar al área de buffer que sea mayor o igual que ‘len’. 69 Documento creado por Slalen para Electronics Strange World Salida: El número de bytes copiados al buffer. Efectos secundarios: el acceso a la variable pública usbgen_rx_len se actualiza con el número de bytes copiados al buffer. Una vez llamado USBGenRead, la recuperación de usbgen_rx_len se puede hacer llamando la macro mUSBGenGetRxLength(). USBGenRead copia un string de los bytes recibidos a través de un Endpoint OUT a una localización especificada por el usuario. Es una función que espera a recibir los datos si no están disponibles. Devuelve ‘0’ para notificar que no hay datos disponibles. Nota: Si el número actual de bytes recibidos es mayor que el número de bytes esperados (len), sólo se copian el número de bytes esperados al buffer. Si el número de bytes recibidos es menor que el número de bytes esperados (len), se copian los bytes recibidos al buffer. byte USBGenRead(byte *buffer, byte len) { usbgen_rx_len = 0; if(!mUSBGenRxIsBusy()) { Ajusta el número de bytes que se esperan al número de bytes recibidos. if(len > USBGEN_BD_OUT.Cnt) len = USBGEN_BD_OUT.Cnt; Copia los datos de la ram-dual al buffer del usuario for(usbgen_rx_len = 0; usbgen_rx_len < len; usbgen_rx_len++) buffer[usbgen_rx_len] = usbgen_out[usbgen_rx_len]; Prepara la ram-dual para la próximo transacción OUT. USBGEN_BD_OUT.Cnt = sizeof(usbgen_out); mUSBBufferReady(USBGEN_BD_OUT); } end if return usbgen_rx_len; } end USBGenRead #endif def USB_USE_GEN 70 Librerías C18 del USB 2.7. MSD.C: USB ALMACENAMIENTO MASIVO En este archivo se han creado todas las funciones de transferencia de los datos que desee el usuario en la clase MSD. 2.7.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #include<string.h> #ifdef USB_USE_MSD 2.7.2. VARIABLES #pragma udata byte MSD_State; Toma valores MSD_WAIT, MSD_DATA_IN o SD_DATA_OUT USB_MSD_CBW gblCBW; byte gblCBWLength; SDCSTATE gblFlag; RequestSenseResponse gblSenseData; byte *ptrNextData; El número de bloques o la longitud son globales porque para cada comando READ_10 y WRITE_10 se tiene que verificar que el último LBA es menor que gblNumBLKS. DWORD gblNumBLKS=0x00,gblBLKLen=0x00; Respuesta estándar para saber que commando está en la ROM. const rom InquiryResponse inq_resp = { 0x00, Dispositivo periférico conectado, acceso directo al bloque del 0x80, dispositivo removible. 0x04, versión = 00=> no cumple ningún estándar, 4=> SPC-2 0x02, respuesta en formato especificado por SPC-2 0x20, n-4 = 36-4=32= 0x20 0x00, sccs etc. 0x00, bque=1 y cmdque=0, indica que la cola simple 00 está obsoleta, pero en el caso de otro dispositivo, usamos 00 0x00, 00 obsoleto, 0x80 para tareas básicas de cola "Microchp", este es el T10 asignado ID del fabricante "Mass Storage ", "0001" }; 2.7.3. PROTORIPOS PRIVADOS void MSDCommandHandler(void); void MSDInquiryHandler(void); 71 Documento creado por Slalen para Electronics Strange World void MSDReadCapacityHandler(void); void MSDReadHandler(void); void MSDWriteHandler(void); void MSDModeSenseHandler(void); void MSDMediumRemovalHandler(void); void MSDRequestSenseHandler(void); void MSDTestUnitReadyHandler(void); void MSDVerifyHandler(void); void MSDStopStartHandler(void); byte IsMeaningfulCBW(void); byte IsValidCBW(void); void PrepareCSWData(void); void SendData(byte*, byte); void SendCSW(void); void ResetSenseData(void); void MSDDataIn(void); void MSDDataOut(void); extern SDC_Error MediaInitialize(SDCSTATE*); extern void SocketInitialize(void); extern SDC_Error SectorRead(dword, byte*); extern SDC_Error SectorWrite(dword, byte*); extern SDC_Error CSDRead(void); extern int DetectSDCard (void); extern byte IsWriteProtected(void); 2.7.4. DECLARACIONES #pragma code 2.7.5. RESPUESTAS ESPECÍFICAS DE LA CLASE 2.7.5.1. void USBCheckMSDRequest(void) Esta rutina une el RESET estándar y el comando de repuesta recibido en el EP0 de control GET_MAX_LUN. 2.7.5.2. void ProcessIO(void) Precondición: Se han llamado MSDInitEP() y SDCardInit(). MSDInitEP() se llama desde USBStdSetCfgHandler(void)(usb9.c) SDCardInit() se llama desde InitializeSystem() en main.c Esta rutina se llama desde loop continuos de main.c. 72 Librerías C18 del USB Todos los comandos de transporte Bulk en el Endpoit 1 se unen aquí: MSD_State contiene el estado actual del módulo de almacenamiento masivo. En el estado MSD_WAIT: Espera al bloque de comando de cubierta (CBW) del Endpoint1. Si se recibe un CBW válido y significativo, dependiendo del comando recibido MSD_State cambia a MSD_DATA_IN si el dato se envía al host (para todos comandos además del WRITE_10). MSD_DATA_OUT si el host está esperando a mandar datos (sólo en el caso de WRITE_10). Al finalizar el comando de estado de transferencia de datos de cubierta (CSW) se envía llamando SendCSW(). 2.7.5.3. void MSDInitEP(void) Esta rutina se llama desde USBStdSetCfgHandler(void) inicializa Bulk-IN y Bulk-OUT, los Endpoints MSD_BD_IN y MSD_BD_OUT Tamaño = 64B (Ver usbmmap.c y usbdefs_std_dsc.h para las definiciones de los Enspoints. 2.7.5.4. void SDCardInit(void) Efectos secundarios: gblFlag se actualiza de acuerdo con la Inicialización. MSD_State se fija a MAD_WAIT. Esta rutina se llama desde InitializeSystem() en main.c. Inicializa la tarjeta SD y si tiene algún problema en la inicialización todos los LEDs se encienced. También fija MSD_State = MSD_WAIT 2.7.5.5. void MSDCommandHandler(void) Esta rutina se llama desde ProcessIO() cuando MSD_State=MSD_WAIT. Esta función decodifica el comando CBW y actua consecuentemente. Si el CBW no está soportado se fija el dato Sense, el estado de CSW se pone en comando fallido (bCSWStatus=01h). 2.7.5.6. void SendCSW(void) Esta función envía el CSW y fija el estado a MSD_WAIT. También cambia MSD_BD_OUT para que apunte a msd_csw (estructura para leer CSW). Notar que esto ha cambiado en el estado MSD_DATA_OUT para que apunte a msd_buffer para que lea datos del host. 2.7.5.7. void SendData(byte* dataAddr, byte dataSize) Esta función envía “dataSize” bytes de datos empezando en la dirección “dataAddr”. 2.7.5.8. void MSDDataIn(void) Esta función envía 512B de datos en el msd_buffer al host en trozos de 64B usando MSD_BD_IN. Hay varias condiciones; cuando los datos que se envían son menores que MSD_EP:SIZE y cuando se comprueba la condición de error bCSWStatus=0x01. En caso de error 0 se llenan y se envían los datos del tamaño esperado por el host dCBWDataTransferLength is sent. 73 Documento creado por Slalen para Electronics Strange World 2.7.5.9. void IsValidCBW() Comprueba si el CBW es válido de acuerdo con las especificaciones de la clase almacenamiento masivo. Se considera un CSW válido si: 1. Se recibe en estado MS_WAIT 2. La longitud de CBW es 1Fh (MSD_CBW_SIZE) 3. dCBWSignature es igual a 0x43425355h 2.7.5.10. void IsMeaningfulCBW() Comprueba si el CBW recibido es significativo de acuerdo con las especificaciones de la clase almacenamiento masivo. Un CSW se considera significativo si: 1. No se fijan bits reservados 2. bCBWLUN contiene un LUN válido soportado por el dispositivo 3. bCBWCBLength y CBWCB tienen concordancia bInterfaceSubClass 2.7.5.11. con void PrepareCSWData() Esto prepara el dato de estado del CSW copiando el dCSWTag del CBWTage y fijando la firma de válido CSW=53425355h 2.7.5.12. void MSDInquiryHandler(void) Esta función prepara la repuesta del comando INQUIRY. La repuesta se copia de la ROM al msd_buffer y CSWStatus, se fijan los valores CSWDataResidue. 2.7.5.13. void ResetSenseData(void) Esta rutina resetean el dato RequestSenseResponse gblSenseData. 2.7.5.14. Sense, inicializando la estructura void MSDReadCapacityHandler() Esta función procesa el dato del registro CSD (leido durante la inicialización de la tarjeta SD) para encontrar el número de bloques (gblNumBLKS) y la longitud del bloque (gblBLKLen). Este dato se copia a msd_buffer y se prepara para responder al comando Leer Capacidad. 2.7.5.15. void MSDReadHandler(void) Decodifica el CBWCB del comando READ(10) para calcular el inicio LBA y la longitud de la transferencia (número de bloques a leer). Leyendo bloques de 512B de datos de la tarjeta SD en msd_buffer (llamando SectorRead). Si se lee satisfactoriamente (sdcValid), los datos se envían al host en trozos de 64B (MSD_IN_EP_SIZE) (ver MSDDataIN()). Esto se repite para el número de bloques TransferLength. En el caso de error bCSWStatus se fija a 0x01 y dato Sense con la clave de dato NOT READY y se prepara el código apropiado ASC, ASCQ. 74 Librerías C18 del USB 2.7.5.16. void MSDDataOut(void) Efectos secundarios: MSD_BD_OUT.ADR se incrementa MSD_OUT_EP_SIZE (para leer los 64B siguientes en msd_buffer). con Esta función lee 64B (MSD_OUT_EP_SIZE) de EP1 OUT MSD_BD_OUT. 2.7.5.17. void MSDWriteHandler() Decodifica el CBWCB del comando WRITE(10) para calcular el comienzo de LBA y la longitud de la transferencia (número de bloques que se escriben). Lee los bloques TransferLength de datos, 1 bloque=512B en un momento en msd_buffer. Los datos del host, 64B en MSD_BD_OUT, se reciben en el msd_buffer (ver MSDDataOut()). El puntero MSD_BD_OUT.ADR se manipula para llenar los 512B del msd_buffer y cuando todos los datos se escriben en la tarjeta SD llamando la función Sector Write(…) (ver sdcard.c). En caso de error bCSWStatus se fija a 0x01 y dato Sense con la clave de dato NOT READY se prepara el código apropiado ASC, ASCQ. 2.7.5.18. void MSDRequestSenseHandler(void) Esta función prepara el Dato Sense para responder al comando Respuesta Sense. El contenido de la estructura RequestSenseResponse se copia a msd_buffer y se fija un satisfactorio bCSWStatus=0x00. 2.7.5.19. void MSDModeSenseHandler() Esta función prepara para responder al comando Modo Sense. Se implementa una respuesta básica en esta versión del código 00h y 0x03 es el tamaño del dato (en bytes) que sigue. 2.7.5.20. void MSDMediumRemovalHandler() Esta función prepara la respuesta al comando Prevent Allow Medium Removal. No se espera una respuesta de datos sólo se espera un CSW con comando de ejecución de estado. Como no se puede controlar la retirada del medio, repondemos con un Success CSW. 2.7.5.21. void MSDTestUnitReadyHandler() Esta función prepara la respuesta al comando Test Unit Ready. No se espera respuesta de datos, sólo se envía un CSW basado en el estado actual de la tarjeta SD se fija un valor de estado de error o de satisfactorio. 2.7.5.22. void MSDVerifyHandler() Esta función prepara la respuesta al comando Verify. No se espera respuesta de datos, respondemos con un CSW satisfactorio. El comando no se procesa en esta versión del código. 2.7.5.23. void MSDStopStartHandler() Esta función prepara la respuesta al comando Start Stop Unit. No se espera respuesta de datos, respondemos con un CSW satisfactorio. El comando no se procesa en esta versión del código. 75 Documento creado por Slalen para Electronics Strange World 2.8. CDC.C: USB DISPOSITIVO DE COMUNICACIÓN En este archivo se han creado todas las funciones de transferencia de los datos que desee el usuario en la clase CDC. 2.8.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #ifdef USB_USE_CDC 2.8.2. VARIABLES #pragma udata byte cdc_rx_len; Longitud total rx byte cdc_trf_state; POINTER pCDCSrc; POINTER pCDCDst; byte cdc_tx_len; byte cdc_mem_type; Estados definidos en cdc.h Puntero dedicado a la fuente Puntero dedicado al destino Longitud total tx _ROM, _RAM LINE_CODING line_coding; Buffer para almacenar líneas de información CONTROL_SIGNAL_BITMAP control_signal_bitmap; SEND_ENCAPSULATED_COMMAND y GET_ENCAPSULATED_RESPONSE se necesitan para responder de acuerdo a las especificaciones CDC. Sin embargo, realmente no se empieza a usar aquí, se utiliza un buffer por comodidad. #define dummy_length 0x08 byte dummy_encapsulated_cmd_response[dummy_length]; 2.8.3. DECLARACIONES #pragma code 2.8.4. 2.8.4.1. RESPUESTAS ESPECÍFICAS DE LA CLASE void USBCheckCDCRequest(void) Esta rutina chequea el paquete de datos setup para ver si este sabe como manipularlo. 76 Librerías C18 del USB 2.9. API DEL USUARIO 2.9.1.1. void CDCInitEP(void) CDCInitEP inicializa los Endpoints CDC, buffer descriptores, estados internos máquina y variables. Se tiene que llamar después de que el host haya enviado una repuesta SET_CONFIGURATION. Ver USBStdSetCfgHandler() en usb9.c para ejemplos. 2.9.1.2. byte getsUSBUSART(char *buffer, byte len) Precondición: El valor del argumento de entrada ‘len’ tiene que ser menor que el tamaño máximo del Endpoint responsable de la recepción de datos bulk del host para la clase CDC. El argumento de entrada ‘buffer’ tiene que apuntar a un área mayor o igual que el tamaño especificado por ‘len’. Entrada: Buffer: Puntero a donde se guardan los datos recibidos. len: El número de bytes esperados. Salida: El número de bytes copiados al buffer. Efectos secundarios: Se actualiza la variable de acceso público cdc_rx_len con el número de bytes copiados al buffer. Para recuperar esta variable llamamos a la macro mCDCGetRxLength(). getsUSBUSART copia un string de bytes recibidos a través del Endpoint OUT CDC Bulk a una localización especificada por el usuario. Es una función de no bloqueo. No espera a los datos si no están disponibles. Devuelve un ‘0’ para notificar que no hay datos disponibles. Nota: Si el número actual de bytes recibidos es mayor que el número de bytes esperados (len), sólo se copian el número de bytes esperados. En cambio, si es menor el número de los recibidos, se copian todos. 2.9.1.3. void putsUSBUSART(char *data) Precondición: cdc_trf_state tiene que estar en el estado CDC_TX_READY. El string de caracteres que apunta ‘data’ tiene que ser igual o menor de 255bytes. Entrada: data: Puntero a un string de datos terminado con nulo. Si no se encuentra, se envían 255bytes al host. putsUSBUSART escribe un string de datos al USB incluyendo caracteres nulos. Utilizar esta versión, ‘puts’, para transferir datos localizados en la memoria de datos. Nota: El mecanismo de transferencia para dispositivo-a-host (put) es más flexible que el de host-a-dispositivo (get). Puede manipular un string de datos mayor que el tamaño máximo del Endpoint In bulk. Se utiliza un estado máquina para transferir un long string de datos a través de múltiples transacciones USB. Ver CDCTxService() para más detalles. 2.9.1.4. void putrsUSBUSART(const rom char *data) Precondición: cdc_trf_state tiene que estar en el estado CDC_TX_READY. 77 Documento creado por Slalen para Electronics Strange World El string de caracteres que apunta ‘data’ tiene que ser igual o menor de 255bytes. Entrada: data: Puntero a un string de datos terminado con nulo. Si no se encuentra, se envían 255bytes al host. putrsUSBUSART escribe un string de datos al USB incluidos los caracteres nulos. Utilizar esta versión, ‘puts’, para transferir datos localizados en la memoria de programa. Nota: El mecanismo de transferencia para dispositivo-a-host (put) es más flexible que el de host-a-dispositivo (get). Puede manipular un string de datos mayor que el tamaño máximo del Endpoint In bulk. Se utiliza un estado máquina para transferir un long string de datos a través de múltiples transacciones USB. Ver CDCTxService() para más detalles. 2.9.1.5. void CDCTxService(void) CDCTxService une las transacciones dispositivo-a-host. Hay que llamar a esta función una vez por cada loop del programa Main. 78 Librerías C18 del USB 2.10. HID.C: USB INTERFAZ CON HUMANOS En este archivo se han creado todas las funciones de transferencia de los datos que desee el usuario en la clase HID. 2.10.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" #include "system\usb\usb.h" #ifdef USB_USE_HID 2.10.2. VARIABLES #pragma udata byte idle_rate; byte active_protocol; byte hid_rpt_rx_len; 2.10.3. [0] Protocolo de inicio [1] Protocolo de informe PROTOTIPOS PRIVADOS void HIDGetReportHandler(void); void HIDSetReportHandler(void); 2.10.4. DECLARACIONES #pragma code 2.10.5. 2.10.5.1. RESPUESTAS ESPECÍFICAS DE LA CLASE void USBCheckHIDRequest(void) Esta rutina chequea el paquete de datos específico para ver si sabe como manipularlo. 2.10.6. 2.10.6.1. API DEL USUARIO void HIDInitEP(void) HIDInitEP inicializa los Endpoints HID, buffer descriptores, estados internos máquina y variables. Se tiene que llamar después de que el host haya enviado una repuesta SET_CONFIGURATION. Ver USBStdSetCfgHandler() en usb9.c para ejemplos. 79 Documento creado por Slalen para Electronics Strange World 2.10.6.2. void HIDTxReport(char *buffer, byte len) Precondición: mHIDTxIsBusy() tiene que devolver falso. El valor de ‘len’ tiene que ser menor o igual que HID_INT_IN_EP_SIZE. Para un Endpoint interrupción, el tamaño del buffer máximo es de 64bytes. Entrada: buffer: Puntero al comienzo de la localización de bytes de datos. len: Número de bytes que se van a transferir. Utilizar esta macro para tranferir datos localizados en la memoria de datos. Aplicación típica: if(!mHIDTxIsBusy()) HIDTxReport(buffer, 3); 2.10.6.3. byte HIDRxReport(char *buffer, byte len) Precondición: El valor del arguemento de entrada ‘len’ tiene que ser menor que el tamaño máximo del Endpoint responsable de la recepción de datos del host USB para la clase HID. El argumento de entrada ‘buffer’ tiene que apuntar a un área mayor o igual al tamaño especificado por ‘len’. Entrada: buffer: Puntero al lugar donde se guardan los datos recibidos. len: Número de bytes que se esperan. Salida: Número de bytes copiados al buffer. Efectos secundarios: Se actualiza la variable de acceso público hid_rpt_rx_len con el número de bytes copiados al buffer. Para recuperar esta variable llamamos a la macro mHIDGetRptRxLength(). HIDRxReport copia un string de bytes recibidos a través del Endpoint OUT HID a una localización especificada por el usuario. Es una función de no bloqueo. No espera a los datos si no están disponibles. Devuelve un ‘0’ para notificar que no hay datos disponibles. Nota: Si el número actual de bytes recibidos es mayor que el número de bytes esperados (len), sólo se copian el número de bytes esperados. En cambio, si es menor el número de los recibidos, se copian todos. 80 Librerías C18 del USB 2.11. MAIN.C Este es el archivo de inicio. En él se incluyen las funciones necesariar para establecer la comunicación USB y las relativas a la aplicación del usuario. 2.11.1. INCLUYE #include <p18cxxx.h> #include "system\typedefs.h" Requerido #include "system\usb\usb.h" Requerido #include "io_cfg.h" Requerido #include "system\usb\usb_compile_time_validation.h" Opcional #include "user\user_mouse.h" Modificable 2.11.2. VARIABLES #pragma udata 2.11.3. PROTOTIPOS PRIVADOS static void InitializeSystem(void); void USBTasks(void); 2.11.3.1. Remapeo de vectores extern void _startup (void); #pragma code _RESET_INTERRUPT_VECTOR = 0x000800 void _reset (void) { _asm goto _startup _endasm } 2.11.3.2. Declaraciones #pragma code void main(void) void main(void) { InitializeSystem(); while(1) { USBTasks() ProcessIO(); } end while } end main USB Tasks Aqui se llama al programa del usuario static void InitializeSystem(void) 81 Documento creado por Slalen para Electronics Strange World InitializeSystem es una rutina centralizada de inicialización. Todas las rutinas de inicialización se llaman desde aquí. static void InitializeSystem(void) { ADCON1 |= 0x0F; Por defecto todos los pines en digital #if defined(USE_USB_BUS_SENSE_IO) tris_usb_bus_sense = INPUT_PIN; Ver io_cfg.h #endif #if defined(USE_SELF_POWER_SENSE_IO) tris_self_power = INPUT_PIN; #endif mInitializeUSBDriver(); Ver usbdrv.h UserInit(); inicialización del usuario } end InitializeSystem void USBTasks(void) Precondición: Se tiene que haber llamado InitializeSystem Da vueltas dando servicio a las tareas USB. void USBTasks(void) { Da servicio al Hardware USBCheckBusStatus(); if(UCFGbits.UTEYE!=1) USBDriverService(); se tiene que utilizar el método obtener Método interrupción u obtener } end USBTasks 82 Librerías C18 del USB 2.12. INTERRUPT.C En este archivo se declaran la parte del programa que se ejecuta durante una interrupción. 2.12.1. INCLUYE #include <p18cxxx.h> #include "system/typedefs.h" #include "system/interrupt/interrupt.h" 2.12.2. VECTORES DE INTERRUPCIÓN #pragma code high_vector=0x08 void interrupt_at_high_vector(void) { _asm goto high_isr _endasm } #pragma code #pragma code low_vector=0x18 void interrupt_at_low_vector(void) { _asm goto low_isr _endasm } #pragma code 2.12.3. 2.12.3.1. DECLARACIONES void high_isr(void) #pragma interrupt high_isr void high_isr(void) { } 2.12.3.2. void low_isr(void) void low_isr(void) { } #pragma code 83