ESCUELA SUPERIOR DE INGENIEROS DE SEVILLA
INGENIERÍA EN AUTOMÁTICA Y ELECTRÓNICA INDUSTRIAL PROYECTO FINAL DE CARRERA DISE ÑO E IMPLE IMPLE ME NTA NTA CIÓN DE UNA T AR J E T A DE CONT CON T R O L C ON COMU COMUNICA C IÓN IÓN US US B
Tutor: José Luís Mora Jiménez Autor: Fernando Bueno Zambruno Sevilla, septiembre de 2013
Fernando Bueno Zambruno
Ingeniero en Automática y Electrónica Industria Industria
Proyecto Final de de Carrera
2
Fernando Bueno Zambruno
Ingeniero en Automática y Electrónica Industria Industria
Proyecto Final de de Carrera
2
Fernando Bueno Zambruno
Proyecto Final de de Carrera
INDICE DE CONTENIDOS Introducción Objeto del proyecto Diseño de la tarjeta Diseño del esquemático Etapa de alimentación Etapa del microcontrolador Diseño de la PCB Programación del microcontrolador Aplicación del PC Diseño de la aplicación Funcionamiento Funcionamiento del sistema Funciones de comunicación Diseño de los controles Control Calefactor y de Humidificador Humidificador Control temperatura y control humedad Presupuesto Recursos Humanos Coste de los componentes y fabricación del PCB Coste Total Conclusión Bibliografía ANEXO 1:Programas del microcontrolador APLICACION_HID.c APLICACION_HID.h shtxx.h Descriptor_easyHID.h ANEXO 2:Programas de la aplicación del PC Form1.cs EasyHID.cs ANEXO 3: Bibliotecas de CCS ANEXO 4: USB- Firmware para dispositivo esclavo
…………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… …………………………………… ……………………………………
5 5 7 10 10 11 14 15 16 16 18 18 23 23 23 27 27 27 28 30 31 32 32 35 36 41 47 47 61 63 66
USB de Clase HID
Ingeniero en Automática y Electrónica Industria Industria
3
Fernando Bueno Zambruno
Ingeniero en Automática y Electrónica Industria
Proyecto Final de Carrera
4
Fernando Bueno Zambruno
Proyecto Final de Carrera
INTRODUCCION La evolución de la microelectrónica desde principios de los años setenta permite la aplicación de los microcontroladores a unos costes razonables, en todas las funciones de la cadena productiva. Así prácticamente en las máquinas modernas de todo tipo de industrias se acoplan microcontroladores para automatizar parcial o totalmente su ciclo productivo, y a la vez efectuar una recogida de datos para gestión de la producción y realizar análisis y estudios técnicos de fabricación. Las técnicas actuales de fabricación apoyadas de los microcontroladores , permite el diseño de instalaciones automáticas según el concepto de fabricación desatendida, es decir, sin necesidad de operarios que vigilen y trabajen continuamente a pie de máquina.
Objeto del proyecto La redacción del presente proyecto es meramente educacional y su propósito es la de culminar la finalización de los estudios de ingeniería. Así mismo, el objetivo del proyecto no ha sido el desarrollo de una aplicación para cubrir una necesidad especifica de control, sino la demostración de la capacidad de diseño para buscar una solución a una posible necesidad de control, en otras palabras, la aplicación desarrollada no va a ser de aplicación, puesto que el problema planteado es completamente ficticio. Así el proyecto que aquí se expone, versa sobre el diseño de una aplicación de control de diversas variables, comprendiendo este tanto el diseño y construcción de una tarjeta electrónica como la de una aplicación informática. Las funciones que desempeñará la tarjeta será: Comunicar con el sensor que captará las variables ambientales. Adaptar los valores de las variables para su transmisión. Establecer el protocolo de comunicación con el ordenador. Recibir las instrucciones para el control de las variables. Comunicar con los elementos finales la respuesta oportuna, como resultado de la acción de control. La aplicación informática se encargará de :
Ingeniero en Automática y Electrónica Industria
5
Fernando Bueno Zambruno
Proyecto Final de Carrera
Realizar la comunicación con la tarjeta . Visualizar los valores de las variable. Establecer las respuestas adecuadas según los parámetros de cada control. Transmisión de las respuestas hacia la tarjeta.
Hay que señalar que el proyecto tiene un emplazamiento es ficticio, además no se ha realizado maqueta alguna que simule una ubicación de los controles, enfatizado más en el carácter didáctico de la construcción de la aplicación, y queda fuera de este proyecto la sintonización de los controles. No obstante, esta afección no supone ningún impedimento para su posible uso práctico futuro.
Ingeniero en Automática y Electrónica Industria
6
Fernando Bueno Zambruno
Proyecto Final de Carrera
DISEÑO DE LA TARJETA Bajo las premisas marcadas anteriormente, pasamos a la descripción de la tarjeta diseñada, remarcando y posteriormente ampliando los aspectos más importante. El núcleo de la tarjeta, va ha ser un microcontrolador, que deberá acometer las funciones de:
Comunicación con el PC. Comunicación con el sensor. Enlace con los elementos finales.
Así se ha seleccionado el microcontrolador PIC 18F2550 de la casa Microchip. Este tiene una importante característica y es la integración de un transceptor de USB 2.0. Aprovechando esta característica se realizó la tarjeta de adquisición basada en este tipo de puerto; Este tiene tres formas distintas de ser utilizado: HID (Poca Velocidad, no necesita instalar drivers), CDC (Velocidad Media, Instala un driver suministrado por el compilador), DLL PIC 18F2550 (Alta velocidad, Instala driver y requiere uso de una DLL, ambos suministrados por Microchip). Se optó por la opción HID (Human Interface Device ) debido a que este tipo de dispositivos implementados por la clase HID es soportado por la mayoría de Sistemas Operativos modernos implementando los controladores necesarios para poder comunicarse con ellos sin ser necesaria la instalación de ningún driver adicional por parte del usuario .
Ingeniero en Automática y Electrónica Industria
Figura 1.
7
Fernando Bueno Zambruno
Por otro lado, para la elección de los sensores que debe tomar los datos de temperatura y humedad se ha optado por un sensor digital, ya que existen numerosos modelos en el mercado y que a la vez aúnan las lecturas de ambas variables en un mismo sensor. La elección final del sensor fue para el SHT75 de la casa Sensirion, que es capaz de obtener ambos datos, con una gran precisión y fiabilidad, además se conecta directamente al microcontrolador ya que se comunica mediante un bus I2C, además la casa ofrece una biblioteca en lenguaje C con las funciones necesarias para poder realizar la lectura de este con lo que se elimina la necesidad de utilizar electrónica asociada al acondicionamiento de señal para estas medidas.
Proyecto Final de Carrera
Figura 2.
Como elementos finales, se ha implementado una bombilla como el elemento regulador de temperatura que aportará la energía calorífica, pues es el sistema utilizado por ejemplo, en mucho terrarios. No obstante esta simulará el funcionamiento de un posible calefactor en otros casos. Para el control de la conmutación de la bombilla se ha utilizado un Triac y un OptoTriac con detector de paso por cero, este último como elemento de seguridad, con el fin de aislar la parte de continua de la de alterna. Para regular la humedad se estuvieron barajando varias opciones, dadas por los diferentes tipos de sistemas de aspersión de suministro de nieblas de humedad que se pueden encontrar en los invernaderos, para finalmente y tras la consulta a mi tutor, se ha optado simplemente por montar un led indicador de funcionamiento en la alimentación del emplazamiento de la posible bomba de aspersión, ya que no tiene mucho sentido haber echo un desembolso económico en este sentido ya que el sistema como inicialmente indicamos es meramente formativo.
Ingeniero en Automática y Electrónica Industria
8
Fernando Bueno Zambruno
Proyecto Final de Carrera
Para controlar el flujo de aire caliente y la humedad en el ambiente se ha hecho uso de dos ventiladores de tipo PC de 12V. Su control se ha decidido realizar en PWM dado que el propio microcontrolador lleva incluidos dos módulos para este fin. Un control en analógico en este caso hubiera implicado utilizar electrónica extra sin aportar ningún tipo de ventaja tangible. La frecuencia del PWM ronda los 90KHz, lo que nos proporcionará un sistema con suficiente ancho de frecuencia para el control de la velocidad de ambos ventiladores. Figura 3.
Ingeniero en Automática y Electrónica Industria
9
Fernando Bueno Zambruno
Proyecto Final de Carrera
DISEÑO DEL ESQUEMÁTICO Para realizar el diseño electrónico y el enrrutado de la placa se ha utilizado el software Eagle en su versión educacional, que se distribuye sin coste pero tiene mermadas algunas de sus capacidades. Así en nuestra placa ha quedado fuera la colocación del transformador, pues las dimensiones de la placa con la que nos permite trabajar son menores que la finalmente hemos realizado. No obstante esto no ha supuesto obstáculo importante. A continuación se van a explicar cada una de las partes que componen el esquemático de la placa controladora de adquisición de datos.
Etapa de alimentación Esta parte del circuito es la encargada de rectificar y adecuar la señal de entrada de la red de 220VCA a los 12VDC y 5VDC necesarios para el circuito. En ella comentar que el transformador que conectará a la red de suministro eléctrico, se le ha colocado un interruptor en el cable que conecta con él, aunque este no aparece en el esquema. La potencia del transformador utilizado es de 13VA de potencia máxima de salida. El puente de diodos realiza la rectificación de la señal alterna de salida del transformador y los condensadores a la salida de este eliminaran el rizado de la señal rectificada. Una vez se tiene la tensión estabilizada se introduce en los estabilizadores de tensión 7812 y 7805 y sus condensadores para eliminación de ruido asociados con lo que se obtiene una señal limpia para alimentar al circuito.
Figura 4. Ingeniero en Automática y Electrónica Industria
10
Fernando Bueno Zambruno
Proyecto Final de Carrera
Etapa del microcontrolador A continuación se muestra la parte de la placa destinada al control del sistema, en la siguiente imagen se puede ver la disposición de las E/S del PIC, cabe destacar que ha sido utilizado un cristal de 20Mhz a partir del cual, y aprovechando la capacidad del divisor de frecuencia interno del microcontrolador se puede llegar a generar los 48MHz necesarios para obtener la velocidad máxima del puerto USB 2.0. El condensador C3 es utilizado como filtro de baja frecuencia para el transceptor USB interno del microcontrolador, el cual trabaja a 3.3V. También se ha colocado un botón de Reset, que reinicializa el funcionamiento del microcontrolador, en caso que las condiciones de operación lo requieran. Se ha dispuesto una etiqueta con un nombre descriptivo en cada pin del PIC con la función de expresar el funcionamiento de este de una manera visual. Cabe mencionar la labor de los pines RB2 (Datos) y RB3 (Reloj), ya que podría resultar algo confusa. Estas son las líneas de comunicación del bus serie del sensor SHT75, es un bus propietario, aunque en funcionamiento similar al I2C. Además destacar el uso de una resistencia de pull-up tal y como indican las especificaciones, para compensar la caída de tensión de la señal y que esta pueda ser procesada por el micro sin errores. Añadir además que la funcionalidad de los pines RC4 y RC5 se corresponde con las líneas de datos diferenciales del puerto USB.
Figura 5.
Ingeniero en Automática y Electrónica Industria
11
Fernando Bueno Zambruno
Proyecto Final de Carrera
Otra parte importante del apartado de la etapa de control es la encargada de transformar el PWM de salida del microcontrolador de 5VDC a los 12VDC necesarios para el funcionamiento del ventilador.
Figura 6.
Para ello se ha configurado el transistor como interruptor, de tal forma que cuando reciba un „1‟ lógico por el terminal de „base‟ entre en saturación y circule corriente entre colector y emisor haciendo que de esta forma se alimente el ventilador. Importante es el uso de los diodos D1, D2 y D3, con ellos se pretende hacer circular la energía acumulada en la bobina del ventilador o de la bomba de humidificación después del corte y así evitar que sea destruido el transistor por una corriente inversa. El transistor BD135 fue seleccionado pensando que debería soportar una frecuencia de conmutación no superior a 100KHz, y que debería soportar una corriente entre emisor-colector de al menos un amperio.
El circuito mostrado a continuación es el encargado de realizar la conmutación de la carga de alterna, es decir, la bombilla en nuestro caso o de un calefactor. Se ha utilizado el diagrama ofrecido en el datasheet del optotriac (MOC3041), con la salvedad de que se ha prescindido de la red Snubber, ya que la carga iba a ser completamente
Ingeniero en Automática y Electrónica Industria
12
Fernando Bueno Zambruno
resistiva y por lo tanto no acumularía energía.
Proyecto Final de Carrera Figura 8.
Para concluir con este apartado comentar que el conector USB utilizado en la placa, lleva el pin de alimentación de 5VDC sin conectar a pista alguna, ya que la alimentación de energía de la placa es propia de la misma y no se utiliza la que suministra el propio bus. El conector correspondiente al sensor SHT75 conectan con un cable en cuyo extremo se ubica el sensor, así se dispone de un cierto radio de trabajo para ubicar el sensor en la posición que nos interese. El led bicolor utilizado distingue entre dos estados del sistema, por un lado tenemos que cuando esté Verde, la comunicación con el PC será correcta, mostrándose de color Rojo para el caso contrario.
Ingeniero en Automática y Electrónica Industria
13
Fernando Bueno Zambruno
Proyecto Final de Carrera
DISEÑO DE LA PCB Para obtener la placa física, en primer lugar se realizó el esquemático completo mediante el editor de esquemáticos de EAGLE, para posteriormente realizar el ruteo de la placa en el editor de placas del mismo programa y ayudado mediante la aplicación de ruteo automático de la misma, realizando esta una gran ayuda para el posicionamiento de los componentes. Comentar como ya se indico, el programa se utiliza en versión educativa, por lo que sus opciones están reducidas, así queda fuera de la placa la ubicación del transformador, puesto que las dimensiones de la tarjeta que maneja son pequeñas.
Figura 9.
Ingeniero en Automática y Electrónica Industria
14
Fernando Bueno Zambruno
Proyecto Final de Carrera
Programación del microcontrolador Para establecer la comunicación entre la tarjeta de adquisición y el PC, donde serán procesados los datos y realizados los controladores se ha utilizado el PIC 18F2550 y en especial su particularidad de la comunicación vía USB. La programación de este micro se ha realizado en C con el compilador de CCS, el código fuente se adjunta en el ANEXO I; además este código, las bibliotecas para USB y el del Sensor SHT75 también están incluidas.
Ingeniero en Automática y Electrónica Industria
15
Fernando Bueno Zambruno
Proyecto Final de Carrera
APLICACIÓN DEL PC Diseño de la aplicación Como se ha indicado la tarjeta se conecta a través del puerto USB al PC, para que este realice las labores de control y supervisión del sistema, permitiéndole al usuario la visualización de las variables de proceso así como la capacidad de interactuar con el sistema (arrancando o parando elementos) y modificando los diferentes parámetros de los controladores.
Figura 10.
La interfaz de la aplicación se ha escrito en Visual C# y como entorno de desarrollo se ha utilizado la suite Visual Studio 2010 Express, que es la versión de libre distribución del programa, la cual nos la hemos descargado directamente de la página de Microsoft.
En un primer vistazo de la interfaz, se distinguen 4 zonas delimitadas y que Ingeniero en Automática y Electrónica Industria
16
Fernando Bueno Zambruno
Proyecto Final de Carrera
corresponden:
*Control Calefactor: Reúne esta zona los mandos de accionamiento de la bombilla calefactora y su dispositivo de control.
*Control Humidificador: Reúne esta zona los mandos de accionamiento de la bomba de agua de los difusores y su dispositivo de control.
Figura 11.
*Control de Temperatura: Reúne esta zona el display de visualización de la temperatura y su control con los diferentes parámetros del mismo que pueden ser modificados.
*Control de Humedad: Reúne esta zona el display de visualización de la humedad y su control con sus diferentes parámetros. Figura 12.
Básicamente las zonas de Control Calefactor y Control humidificador se han diseñado de forma similar, al igual que las otras dos restantes Control de Temperatura y Control de Humedad.
Ingeniero en Automática y Electrónica Industria
17
Fernando Bueno Zambruno
Proyecto Final de Carrera
Funcionamiento del sistema
En este apartado se hará una descripción del funcionamiento del sistema, así como una descripción de los elementos de la interface. Para echar a andar nuestro sistema comenzaremos por conectar la tarjeta a suministro eléctrico, En este momento la tarjeta encenderá el led con color rojo indicativo de que tiene energía pero no tiene canal de comunicación con el PC. Para establecer esta, conectaremos el cable USB de la tarjeta en un puerto del PC, en ese momento se producirá la enumeración, consistente en que el Host del PC le pregunta a la tarjeta cuales son sus parámetros para identificarlo (VendorID y ProductID), así como para asignarle una dirección al dispositivo para permitir la transferencia de datos. Una vez realizado esto y si todo ha ido bien, el microcontrolador apagará el led en rojo y encenderá el led en verde. Ahora arrancamos nuestra aplicación y la interfaz gráfica se mostrará. Si pulsamos el botón de Conectar Dispositivo el programa buscará un descriptor de dispositivo que coincida con los valores que tiene prefijados de VendorID y ProductID y si los encuentra activará el sistema de notificaciones y la transferencia de datos comenzará. Una vez el sistema se ha puesto en marcha, las indicaciones de la temperatura como de la humedad empiezan a ser visualizadas y los distintos elementos se muestran en reposo con sus controles en la posición de manual pudiéndose desde ese instante el usuario interaccionar con el sistema.
Funciones de comunicación Para dotar a nuestra aplicación de la comunicación mediante el puerto USB con la tarjeta, hemos tenido que definir la clase de comunicación que se implantará y que en nuestro caso será mediante protocolo HID (Human Interfase Devies). La clase nos definen las funciones de comunicación que utilizan el conjunto de dispositivos USB y que proveen al Host (PC) de las mismas. Por ello, se ha agregado la clase easyHID.cs al proyecto de nuestra aplicación (Visual C#) y a través de ella podemos llamar a las diferentes funciones de control que se encuentran en mcHID.dll, una librería proporcionada por la empresa MecaniqueUK para el control de dispositivos USB mediante protocolo HID. - mcHID.dll (Librería de control ). - easyHID.cs (Clase con las funciones de control definidas). Ingeniero en Automática y Electrónica Industria
18
Fernando Bueno Zambruno
Proyecto Final de Carrera
Más abajo tenemos las llamadas a las diferentes funciones incluidas en la librería y entre ellas podemos destacar las siguientes: [DllImport("mcHID.dll")] public static extern bool Connect(IntPtr pHostWin);
Conecta la aplicación al controlador. [DllImport("mcHID.dll")] public static extern bool Disconnect();
Desconecta la aplicación al controlador. [DllImport("mcHID.dll")] public static extern bool Read(UInt32 pHandle, IntPtr pData);
Lee el reporte de entrada para ver si hay datos provenientes del dispositivo. El tamaño del reporte dependerá de la constante BUFFER_IN_SIZE. La función retorna 1 si hay datos disponibles para leer y 0 si no los hay. [DllImport("mcHID.dll")] private static extern bool Write(UInt32 pHandle, IntPtr pData);
Escribimos un reporte de salida con los datos almacenados en la variable pData , que es un puntero a un buffer no administrado. El tamaño del reporte dependerá de la vari able BUFFER_OUT_SIZE. La función retorna 1 si se ha podido enviar el reporte y 0 en el caso contrario.
[DllImport("mcHID.dll")] public static extern UInt32 GetVendorID(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern UInt32 GetProductID(UInt32 pHandle);
A través de estas funciones obtenemos el vendorID y el productID del dispositivo. [DllImport("mcHID.dll")] public static extern void SetReadNotify(UInt32 pHandle, bool pValue);
Activamos el servicio de notificaciones para recibir mensajes cada vez que se produce un evento de lectura. El servicio se activa una vez que el dispositivo ya se ha conectado al controlador. [DllImport("mcHID.dll")] public static extern bool IsAvailable(UInt32 pVendorId, UInt32 pProductId);
A través de esta función obtenemos el estado de conexión del dispositivo mediante el vendorID y el productID .
Ingeniero en Automática y Electrónica Industria
19
Fernando Bueno Zambruno
Proyecto Final de Carrera
Con esto ya hemos explicado todas las funciones que se usarán en el programa. Ahora veremos el desarrollo de 2 de ellas: Write y Read, correspondientes a las funciones para leer y escribir datos. public static bool Read(UInt32 pHandle, out byte[] pData) { IntPtr unmanagedBuffer = Marshal.AllocHGlobal(BUFFER_IN_SIZE); bool result = Read(pHandle, unmanagedBuffer); try { pData = new byte[BUFFER_IN_SIZE]; Marshal.Copy(unmanagedBuffer, pData, 0, BUFFER_IN_SIZE); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } return result; }
Esta función lee el reporte de entrada para ver si hay datos disponibles. En primera instancia asigna memoria con un tamaño igual a BUFFER_IN_SIZE a un puntero: IntPtr unmanagedBuffer = Marshal.AllocHGlobal(BUFFER_IN_SIZE);
Se utiliza el método Marshal , para poder trabajar con memoria no administrada. Luego guarda el retorno de la función Read en la variable result, que corresponde a la presencia de datos en el reporte de entrada. bool result = Read(pHandle, unmanagedBuffer);
A través del método try , crea un buffer llamado pData . Como es una porción de memoria no administrada, usa Marshal nuevamente y copia los datos usando el puntero creado con anterioridad en el buffer pData, quedando los datos del reporte guardados en este último. try { pData = new byte[BUFFER_IN_SIZE]; Marshal.Copy(unmanagedBuffer, pData, 0, BUFFER_IN_SIZE);
Como el espacio de memoria que al que hacía alusión el puntero no se utiliza más y es memoria no administrada, usamos Marshal para liberarla para otros procesos. finally { Marshal.FreeHGlobal(unmanagedBuffer); }
Finalmente la función retorna con el valor que haya tomado result. return result;
Esta función escribe un dato en el reporte de salida. public static bool Write(UInt32 pHandle, byte[] pData)
Ingeniero en Automática y Electrónica Industria
20
Fernando Bueno Zambruno
Proyecto Final de Carrera
{ IntPtr unmanagedBuffer = Marshal.AllocHGlobal(BUFFER_OUT_SIZE); bool result; try { Marshal.Copy(pData, 0, unmanagedBuffer, BUFFER_OUT_SIZE); result = Write(pHandle, unmanagedBuffer); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } return result; }
En primer instancia es igual que la función anterior, salvo que la variable result se le asigna un valor luego de copiar los datos en el reporte de salida; para poder indicar si los datos se han escrito correctamente. Ahora que ya tenemos la clase easyHID.cs explicada, comenzamos con el núcleo principal del programa. Para saber si el dispositivo estaba conectado/desconectado o enviando datos, necesitábamos leer los diferentes mensajes que entregaba el controlador. Por lo que necesitamos una función que tome los mensajes de Windows, los procese y según el valor que tomen realizar diferentes acciones. Por ello crearemos 3 funciones: - Dispositivo _ conectado (En caso que el dispositivo esté conectado al host ) - Dispositivo _ desconectado (En caso de que desconectemos el dispositivo ) - Lee _ dispositivo (Si se recibe el mensaje de que hay datos disponibles ) El parámetro de cada una de las 3 funciones se da según el mensaje que se recibe por parte del controlador, con lo cual la función queda de la siguiente manera. protected override void WndProc(ref Message message) { // Interceptamos los mensajes de windows. if (message.Msg == EasyHID.WM_HID_EVENT) // Si ha ocurrido algún evento... { switch (message.WParam.ToInt32()) // Intercepta el mensaje y opera según el valor recibido.... { case EasyHID.NOTIFY_PLUGGED: Dispositivo_Conectado((UInt32)message.LParam.ToInt32()); // Se ha conectado un dispositivo. break; case EasyHID.NOTIFY_UNPLUGGED: Dispositivo_desconectado((UInt32)message.LParam.ToInt32()); // Se ha desconectado un dispositivo. break; case EasyHID.NOTIFY_READ: Lee_datos((UInt32)message.LParam.ToInt32()); // Hay datos en el buffer de entrada. break; } } base.WndProc(ref message);
Ingeniero en Automática y Electrónica Industria
21
Fernando Bueno Zambruno
Proyecto Final de Carrera
}
Ahora tenemos que crear las 3 funciones dentro de la clase principal: private void Dispositivo_Conectado(UInt32 handle){} private void Dispositivo_desconectado(UInt32 handle) {} private void Lee_datos(UInt32 In_handle) {}
Con esto, cada vez que se produzca una notificación podremos realizar diferentes acciones en consecuencia. Para conectar la aplicación al controlador y a su vez el dispositivo a dicho controlador, utilizamos la función: EasyHID.Connect(Handle);
Y para poder ver si realmente estamos conectados con el entrenador USB hacemos uso de la función: if (EasyHID.IsAvailable(EasyHID.VENDOR_ID, EasyHID.PRODUCT_ID) == true)
Con esto podemos detectar cualquier dispositivo conectado que coincida con el VID , PID declarado en la aplicación.
Ingeniero en Automática y Electrónica Industria
22
Fernando Bueno Zambruno
Proyecto Final de Carrera
DISEÑO DE LOS CONTROLES Como se ha comentado anteriormente , los controles denominados como Control Calefactor y Control Humidificador se han diseñado de forma similar, al igual que las otras dos restantes Control de Temperatura y Control de Humedad. Esto se debe a la similitud de la naturaleza de las variables que se van a controlar.
Control Calefactor y de Humidificador Atendiendo a que nuestro proyecto desarrollaría una solución para un sistema real, se han diseñado estos controles suponiendo que los posibles sistemas a controlar fuesen un calefactor en el caso de la temperatura y de una bomba que suministrara agua a unos difusores en el caso del humificador. En ambos casos dichos sistemas se encontrarían accionados mediante su correspondiente relé, los cuales serían accionados por las salidas RB0 y RB1 del microprocesador. Dicho lo cual, la idea con que se han definido ambos controles ha sido la de regular la potencia entregada por los comentados relés, mediante la regulación del ancho de pulso en que se encuentra activado los reles. Para ello se fija un tiempo definido como ciclo de trabajo que será el tiempo en el que se encuentre activo los relés del total del tiempo definido como periodo. Ambos tiempo son definidos por el usuario.
Figura 13.
Control temperatura y control humedad Para estos controles la solución propuesta ha sido la de diseñar unos PID ya que se aplican de manera general en la mayoría de los controles actuales y suponen la solucion más ventajosa frente a tro tipo de controles. Ingeniero en Automática y Electrónica Industria
23
Fernando Bueno Zambruno
Proyecto Final de Carrera
Los controles PID responden a la siguiente ecuación:
Donde e(t) es el error de la señal y u(t) es la entrada de control del proceso. K p es la ganancia proporcional, T i es la constante de tiempo integral y T d es la constante de tiempo derivativa. En el dominio s, el controlador PID se puede escribir como:
El controlador PID tiene tres parámetros (K p, T i, T d ) los cuales interactúan unos con otros y su ajuste suele presentar inconvenientes. Por lo que nos podemos ayudar de las reglas de Ziegler/Nichols que nos proponen unos valores para los parámetros del control PID basado en un análisis del sistema a lazo abierto, ya que no contienen integradores ni polos complejos. Siendo su respuesta a una entrada escalon similar a la de la mostrada en la figura.
Ingeniero en Automática y Electrónica Industria
24
Fernando Bueno Zambruno
Proyecto Final de Carrera
Donde los coeficientes:
Según Zieggler/Nichols, nos definen los parámetros del controlador de la manera:
La realización de un controlador PID discreto viene dado por la transformada z:
Donde podemos sustituir por:
Ingeniero en Automática y Electrónica Industria
25
Fernando Bueno Zambruno
Proyecto Final de Carrera
Donde:
Existen distintas posibilidades para la realización práctica de un controlador PID. Una de las más habituales es la realización en paralelo.
Se debe tener en cuenta que el tiempo de muestreo del sistema debe ser mucho menor al tiempo de establecimiento del sistema en lazo abierto. En el modelo de Ziegler/Nichols se toma un valor de T< τ 0/4 o también puede utilizarse T< γ0/10. Un problema asociado a este tipo de controlador es el llamado “integral windup” el cual puede provocar largos periodos de sobreimpulsos, motivados por los valores excesivos que alcanza la señal de control debido a la acumulación en el integrador. Para evitar este problema hemos limitado la señal de control entre un valor máximo y un valor mínimo, impidiendo que el integrador actúe cuando se superan esos límites.
Ingeniero en Automática y Electrónica Industria
26
Fernando Bueno Zambruno
Proyecto Final de Carrera
PRESUPUESTO Para realizar una estimación del coste económico del proyecto, se ha dividido los gastos del mismo asociándolos a varios grupos diferentes:
Recursos Humanos: Mano de obra necesaria para la realización del proyecto. Componentes y fabricación : Coste de los componentes y fabricación del circuito.
No se han contabilizado recursos hardware necesarios para la realización del proyecto, tales como soldador, multímetro, ordenador, ya que se trata de un proyecto universitario y son recursos a los que se tiene acceso libremente sin coste alguno para cualquier estudiante de la universidad de Sevilla. Tampoco se han tenido en cuenta posibles gastos de software, ya que todo el diseño se ha realizado bajo software de libre distribución.
Recursos Humanos Los recursos humanos del proyecto corren a cargo del proyectista, al que se le aplicará un sueldo de Ingeniero Junior hipotético de 1500€/mes, teniendo en cuenta que en un mes se contabilizan unas 165 horas hábiles, se obtiene un salario de 9,09€/hora. Las tareas realizadas por el ingeniero son el análisis de requisitos, diseño, implementación montaje y teste o de la placa de prueba del PCB. Resumiendo y concretando horas:
Tarea Análisis de requisitos Diseño Implementación Experimentación TOTAL
Horas 25 70 135 50 280
Coste 227,25 636,3 1227,15 454,5 2545,2 €
Coste de los componentes y fabricación del PCB Podemos realizar un presupuesto del proyecto detallando los componentes utilizados: Cantidad
Elemento
Ingeniero en Automática y Electrónica Industria
Precio
IMPORTE 27
Fernando Bueno Zambruno
1 1 1 1 1 1 1 1 1 3 1 1 3 7 1 1 2 2 1 2 2 1 1 1 3 2 1 1 --------
Proyecto Final de Carrera
Unitario SHT75 32,25 Transformador 12,3 PIC 18F2550 5,01 Puente diodo 0,7 Cristal 20MHz 0,27 Conector Hembra USB pla0,6 ca Pulsador 0,41 LED Rojo/Verde 0,18 Zócalo 0,75 Transistor BD437 0,18 Triac BT136 0,68 MOC3041M 1,05 Diodo 1N4446 0,08 Bornas 0,45 Regulador 7805 0,57 Regulador 7812 0,65 Condensador 22pF 0,09 Condensador 100nF 0,12 Condensador electrolítico 0,25 47uF Condensador electrolítico 0,4 470uF Condensador electrolítico 1,05 4700uF Resistencia 330 0,2 Resistencia 360 0,2 Resistencia 180 0,2 Resistencia 2K2 0,04 Resistencia 10K 0,08 Resistencia 1K 0,08 Placa virgen 11,8 TOTAL ------
Ingeniero en Automática y Electrónica Industria
32,25 12,3 5,01 0,7 0,27 0,6 0,41 0,18 0,75 0,54 0,68 1,05 0,24 3,15 0,57 0,65 0,18 0,24 0,25 0,8 2,1 0,2 0,2 0,2 0,12 0,16 0,08 11,8 75,68 €
28
Fernando Bueno Zambruno
Proyecto Final de Carrera
Coste Total Reuniendo los costea anteriores y añadiendo el IVA correspondiente del 21% CONCEPTO Componentes y Fabricación Circuito Recursos Humanos TOTAL IVA 21% TOTAL
Ingeniero en Automática y Electrónica Industria
COSTE 75,68 € 2545,2 € 2620,88 € 550,38 € 3171,26€
29
Fernando Bueno Zambruno
Proyecto Final de Carrera
CONCLUSION El proyecto aquí presentado ha pretendido realizar un control de bajo coste para un problema de control de temperatura y humedad. Para la ejecución del mismo, ha supuesto la unión de varias facetas como son el diseño del hardware con el software de programación de nuestro PIC y la programación de una aplicación informática para la visualización de las variables e interacción con el usuario final. En cuanto a las posibles mejoras a realizar en el futuro, se contempla el registro de los valores de las variables que permitieran el estudio posterior de la evolución de los proceso, lo cual permitiría mejorar los procedimientos de sintonización de los controles, como la implementación de otros sensores para que la lectura de las variables no correspondiera de forma concreta a un punto, sino a la media de varios puntos, lo cual supondría una lectura media de la temperatura de la habitación. Otra mejora a considerar en el futuro sería trasladar las tareas de control desde la aplicación informática a la tarjeta de control. Esta deslocalización del algoritmo implicaría una mayor fiabilidad del sistema, puesto que una pérdida de comunicación entre ambas partes no implicaría la paralización de las tareas de control.
Ingeniero en Automática y Electrónica Industria
30
Fernando Bueno Zambruno
Proyecto Final de Carrera
BIBLIOGRAFIA
José María Angulo Usategui, Susana romero Yesa, Ignacio Angulo Martínez. Mi- crocontroladores PIC. Diseño práctico de aplicaciones. Mc Graw Hill, 2º Edición, 2006.
Páginas web de fabricantes de Microcontroladores www.analog.com www.atmel.com www.microchip.com www.intel.com www.national.com
Tutoriales: Introducción a la Programación del PIC.
Entorno de desarrollo MPLAB www.microchip.com
Características del compilador de C CCS. www.ccsinfo.com
USB Implementers Forum, Inc. http://www.usb.org
Datasheet del microcontrolador PIC18F2550 disponible en www.microchip.com
Jan Axelson. USB Complete . Lakeview Research, 1º Edición 1999
Eduardo García Breijo, Compilador C CCS y simulador Proteus para microcontro- ladores PIC . Marcombo, Ediciones Técnicas, 1º Edición 2008.
Características de Microsoft Visual Basic, Visual C++ y Visual C#. Disponible en Internet. http://www.microsoft.com
PIC18F2550 y USB.Desarrollo de aplicaciones. Moyano Jonathan.
Fco. Javier Ceballos. Visual C#. 3ª Edición. Ed. RAMA
Ingeniero en Automática y Electrónica Industria
31
Fernando Bueno Zambruno
Proyecto Final de Carrera
ANEXO 1:Programas del microcontrolador APLICACION_HID.c /******************************************************************** Programa para medición y control de temperaturas y humedad. Universidad de Sevilla 2013. *********************************************************************/ //========================================================================= #include <18F2550.h> // Definición de registros internos del PIC18F2550. #include "shtxx.h" //Registros del sensor. #include
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN // HSPLL: Utilizaremos un cristal de alta velocidad en conjunto con el PLL. // NOWDT: No vamos a usar el perro guardian. // NOPROTECT: Memoria no protejida contra lecturas. // NOLVP: No utilizamos el modo de programación con bajo voltaje. // NODEBUG: No utilizamos código para debugear. // USBDIV: signfica que el clock del usb se tomará del PLL/2 = 96Mhz/2 = 48Mhz. // PLL1: significa que el PLL prescaler dividirá la frecuencia del cristal. // para HS = 20Mhz, PLL = 5. con esto se obtiene: 20Mhz/5 = 4Mhz. // CPUDIV1: El PLL postscaler decide la división en 2 de la frecuencia de // salida del PLL de 96MHZ, si queremos 48MHZ, lo dejamos como está. // VREGEN: habilita el regulador de 3.3 volts que usa el módulo USB. // NOPBADEN: Todo el Puerto B como I/O digitales. // Usamos una frecuencia de trabajo de 48Mhz. #use delay(clock=48000000) // Incluimos librerías utilizadas por la aplicación. #include // Drivers's USB del PIC18F2550. #include // Definición de funciones y hardware utilizado en el programa. #include // Descriptores HID del proyecto. #include // Funciones del USB. // Usamos fast_io, en los puertos B y C. #use fast_io(b) #use fast_io(c) void USB_debug(){ LED_ON(LED_RED); // Enciende el led error y apaga el led USB_OK. LED_OFF(LED_GREEN); usb_wait_for_enumeration(); // Espera a ser enumerado por el host. LED_ON(LED_GREEN); // Enciende el led USB_OK y apaga el led USB_ERROR. LED_OFF(LED_RED); } float temperatura, humedad;
Ingeniero en Automática y Electrónica Industria
32
Fernando Bueno Zambruno
Proyecto Final de Carrera
void main(void) pal. { // Variables globales. int8 int8 int8 int8
// Función Princi-
recibe[USB_EP1_RX_SIZE]; envia[USB_EP1_TX_SIZE]; valorPWM1=0; valorPWM2=0;
// Declaramos la variable recibe // Declaramos la variable envía // Variable que contiene el valor // Variable que contiene el valor
de 32 bytes. de 32 bytes. del PWM1. del PWM2.
char ctemp[6], chumed[6]; shtxx_init(); set_tris_b(0b11111100); output_b(0x01); set_tris_c(0b00111000);
// RB0, RB1 salidas, el resto entradas. // Inicializamos las salidas a 0, excepto RB0.
setup_ccp1(CCP_PWM); setup_ccp2(CCP_PWM); setup_timer_2(T2_DIV_BY_1, 255, 1); set_pwm1_duty(0); // Ventilador 1 Apagado set_pwm2_duty(0); // Ventilador 2 Apagado usb_init(); usb_task(); USB_debug();
// Inicializamos el stack USB. // Habilita el periferico usb y las interrupciones. // Nos muestra el estado de conección del USB.
while (TRUE) { if(usb_enumerated()) {
// Bucle infinito. // Si el dispositivo está configurado...
//Leemos sensor sht75 read_shtxx(temperatura, humedad); // Pasamos de valores numérico a caracteres sprintf(ctemp,"%2.2f", temperatura); envia[0]=0x00; envia[1]=ctemp[0]; envia[2]=ctemp[1]; envia[3]=ctemp[2]; envia[4]=ctemp[3]; envia[5]=ctemp[4]; envia[6]=ctemp[5]; sprintf(chumed,"%2.2f", humedad); envia[7]=chumed[0]; envia[8]=chumed[1]; envia[9]=chumed[2]; envia[10]=chumed[3]; envia[11]=chumed[4]; envia[12]=chumed[5]; usb_put_packet(1, envia, USB_CONFIG_HID_TX_SIZE, USB_DTS_TOGGLE); quete de datos por USB.
Ingeniero en Automática y Electrónica Industria
// Enviamos el pa-
33
Fernando Bueno Zambruno
Proyecto Final de Carrera
if (usb_kbhit(1)) // Si hay un paquete de datos del host.. en el buffer lo tomamos y guardamos en la variable data. { usb_get_packet(1, recibe, USB_CONFIG_HID_RX_SIZE); // En el buffer lo tomamos del EP1 y lo guardamos en la variable recibe.... if(recibe[0]==ACTIVA_SALIDAS){ // Si recibe comando de control de salidas... switch(recibe[1]){ // Según el dato que recibe, activa o desactiva los led's. case LED_1: output_toggle(PIN_B0); // Cambia de estado el LED1. break; case LED_2: output_toggle(PIN_B1); // Cambia de estado el LED2. break; } } if(recibe[0]==PWM_CONTROL1) // Si recibimos el comando de control PWM1.. {valorPWM1=recibe[2]; set_pwm1_duty(valorPWM1); } // Tomamos el dato y lo procesamos. if(recibe[0]==PWM_CONTROL2) // Si recibimos el comando de control PWM2.. {valorPWM2=recibe[3]; set_pwm2_duty(valorPWM2); }
// Tomamos el dato y lo procesamos.
} } } }
Ingeniero en Automática y Electrónica Industria
34
Fernando Bueno Zambruno
Proyecto Final de Carrera
APLICACION_HID.h // Definición de constantes y funciones:
#define #define #define #define
LED_GREEN LED_RED PWM1 PWM2
PIN_C6 PIN_C7 PIN_C2 PIN_C1
// // // //
Led USB_OK...( Dispositivo conectado al host ). Led USB_ERROR... ( Dispositivo no detectado ). Control PWM1. Control PWM2.
// Constantes varias: #define USB_CONFIG_HID_TX_SIZE #define USB_CONFIG_HID_RX_SIZE
32 // Definimos el tamaño del endpoint de salida. 32 // Definimos el tamaño del endpoint de entrada.
// Comandos de entrada. #define ACTIVA_SALIDAS #define PWM_CONTROL1 #define PWM_CONTROL2
0x0A // Comando para activar LED'S. 0x0B // Comando para activar PWM1. 0x0C // Comando para activar PWM2.
#define LED_1 #define LED_2
0x10 // Cambia de estado el LED 1. 0x20 // Cambia de estado el LED 2.
// Función para prender y apagar led's. #define LED_ON output_high // Función para encender el LED. #define LED_OFF output_low // Función para apagar el LED. void USB_debug();
Ingeniero en Automática y Electrónica Industria
35
Fernando Bueno Zambruno
Proyecto Final de Carrera
shtxx.h
/////////////////////////////////////////////////////////////////////////////// // // // Driver file for SHTxx Temperature & Humidity Sensor // // // // ***** To initialise SHTxx sensor upon power up ***** // // // // Function : sht_init() // // Return : none // // // // // // ***** To measure and caluculate SHTxx temp & humid ***** // // // // Function : read_shtxx (temp, humid) // // Return : temperature & humidity in float values // // // /////////////////////////////////////////////////////////////////////////////// #define shtxx_data PIN_B2 #define shtxx_clk PIN_B3 #use delay(clock=48000000) //***** Function to alert SHTxx ***** void shtxx_transstart(void) { output_float(shtxx_data); output_low(shtxx_clk); delay_us(1); output_high(shtxx_clk); delay_us(1); output_low(shtxx_data); delay_us(1); output_low(shtxx_clk); delay_us(2); output_high(shtxx_clk); delay_us(1); output_float(shtxx_data); delay_us(1); output_low(shtxx_clk); }
//data high //clk low //clk high //data low //clk low //clk high //data high //clk low
//***** Function to write data to SHTxx ***** int1 shtxx_write_byte(int8 iobyte) { int8 i, mask = 0x80; int1 ack; //Shift out command delay_us(4); for(i=0; i<8; i++) { output_low(shtxx_clk); if((iobyte & mask) > 0) output_float(shtxx_data); else output_low(shtxx_data); delay_us(1);
Ingeniero en Automática y Electrónica Industria
//clk low //data high if MSB high //data low if MSB low
36
Fernando Bueno Zambruno
Proyecto Final de Carrera
output_high(shtxx_clk); delay_us(1); mask = mask >> 1;
//clk high //shift to next bit
} //Shift in ack output_low(shtxx_clk); delay_us(1); ack = input(shtxx_data); output_high(shtxx_clk); delay_us(1); output_low(shtxx_clk); return(ack);
//clk low //get ack bit //clk high //clk low
} //***** Function to read data from SHTxx ***** int16 shtxx_read_byte(void) { int8 i; int16 iobyte = 0; const int16 mask0 = 0x0000; const int16 mask1 = 0x0001; //shift in MSB data for(i=0; i<8; i++) { iobyte = iobyte << 1; output_high(shtxx_clk); //clk high delay_us(1); if(input(shtxx_data)) iobyte |= mask1; //shift in data bit else iobyte |= mask0; output_low(shtxx_clk); //clk low delay_us(1); } //send ack 0 bit output_low(shtxx_data); delay_us(1); output_high(shtxx_clk); delay_us(2); output_low(shtxx_clk); delay_us(1); output_float(shtxx_data);
//data low //clk high //clk low //data high
//shift in LSB data for(i=0; i<8; i++) { iobyte = iobyte << 1; output_high(shtxx_clk); //clk high delay_us(1); if(input(shtxx_data)) iobyte |= mask1; //shift in data bit else iobyte |= mask0; output_low(shtxx_clk); //clk low delay_us(1); } //send ack 1 bit output_float(shtxx_data); //data high delay_us(1);
Ingeniero en Automática y Electrónica Industria
37
Fernando Bueno Zambruno
Proyecto Final de Carrera
output_high(shtxx_clk); //clk high delay_us(2); output_low(shtxx_clk); //clk low return(iobyte); } //***** Function to wait for SHTxx reading ***** void shtxx_wait(void) { int16 shtxx_delay; output_float(shtxx_data); //data high output_low(shtxx_clk); //clk low delay_us(1); for(shtxx_delay=0; shtxx_delay<30000; shtxx_delay++) // wait for max 300ms { if (!input(shtxx_data)) break; //if shtxx_data low, SHTxx ready delay_us(10); } } //***** Function to reset SHTxx communication ***** void shtxx_reset(void) { int8 i; output_float(shtxx_data); //data high output_low(shtxx_clk); //clk low delay_us(2); for(i=0; i<9; i++) { output_high(shtxx_clk); //toggle clk 9 times delay_us(2); output_low(shtxx_clk); delay_us(2); } shtxx_transstart(); } //***** Function to soft reset SHTxx ***** void shtxx_softreset(void) { shtxx_reset(); //SHTxx communication reset shtxx_write_byte(0x1e); //send SHTxx reset command delay_ms(15); //pause 15 ms } //***** Function to measure SHTxx temperature ***** int16 shtxx_measuretemp(void) { int1 ack; int16 iobyte;
Ingeniero en Automática y Electrónica Industria
38
Fernando Bueno Zambruno
shtxx_transstart(); ack = shtxx_write_byte(0x03); if(ack == 1) return; shtxx_wait(); iobyte = shtxx_read_byte(); return(iobyte);
Proyecto Final de Carrera
//alert SHTxx //send measure temp command and read ack status //wait for SHTxx measurement to complete //read SHTxx temp data
} //***** Function to measure SHTxx RH ***** int16 shtxx_measurehumid(void) { int1 ack; int16 iobyte; shtxx_transstart(); ack = shtxx_write_byte(0x05); if(ack == 1) return; shtxx_wait(); iobyte = shtxx_read_byte(); return(iobyte);
//alert SHTxx //send measure RH command and read ack status //wait for SHTxx measurement to complete //read SHTxx temp data
} //***** Function to calculate SHTxx temp & RH ***** void shtxx_calc(int16 temp, int16 humid, float &tc, float &rhlin, float &rhtrue) { float rh; //calculate temperature reading tc = ((float) temp * 0.01) - 40.0; //calculate Real RH reading rh = (float) humid; rhlin = (rh * 0.0405) - (rh * rh * 0.0000028) - 4.0; //calculate True RH reading rhtrue = ((tc - 25.0) * (0.01 + (0.00008 * rh))) + rhlin; } //***** Function to measure & calculate SHTxx temp & RH ***** void read_shtxx(float &temp, float &truehumid) { int16 restemp, reshumid; float realhumid; restemp = shtxx_measuretemp(); //measure temp reshumid = shtxx_measurehumid(); //measure RH shtxx_calc(restemp, reshumid, temp, realhumid, truehumid);
//calculate temp & RH
} //***** Function to initialise SHTxx on power-up ***** void shtxx_init(void)
Ingeniero en Automática y Electrónica Industria
39
Fernando Bueno Zambruno
Proyecto Final de Carrera
{ shtxx_reset(); delay_ms(20);
//reset SHTxx //delay for power-up
}
Ingeniero en Automática y Electrónica Industria
40
Fernando Bueno Zambruno
Proyecto Final de Carrera
Descriptor_easyHID.h /////////////////////////////////////////////////////////////////////////// /// Descriptor_easyHID.h /// /// Escrito para funcionar con easyHID.dll /// /////////////////////////////////////////////////////////////////////////// #IFNDEF __USB_DESCRIPTORS__ #DEFINE __USB_DESCRIPTORS__ #ifndef USB_CONFIG_PID #define USB_CONFIG_PID #endif
0x07D0
// PID easyHID, configurado por el usuario.
#ifndef USB_CONFIG_VID #define USB_CONFIG_VID 0x1781 #endif
// VID easyHID, configurado por el usuario.
#ifndef USB_CONFIG_BUS_POWER // El rango válido es 0...500. #define USB_CONFIG_BUS_POWER 100 #endif
// 100mA, corriente máxima que entregará el puerto.
#ifndef USB_CONFIG_VERSION // El número de versión está guardado en el descriptor, en formato bcd. // El rango válido es 00.00 to 99.99 #define USB_CONFIG_VERSION 0x0100 //01.00 #endif #ifndef USB_CONFIG_HID_TX_SIZE // El rango válido es 0-255 #define USB_CONFIG_HID_TX_SIZE #endif #ifndef USB_CONFIG_HID_RX_SIZE // El rango válido es 0-255 #define USB_CONFIG_HID_RX_SIZE #endif
32
32
// Configurado por el usuario según aplicación.
// Configurado por el usuario según aplicación.
#ifndef USB_CONFIG_HID_TX_POLL // for full speed devices, valid range is 1 -255 // for slow speed devices, valid range is 10-255 #define USB_CONFIG_HID_TX_POLL 10 #endif #ifndef USB_CONFIG_HID_RX_POLL // for full speed devices, valid range is 1 -255 // for slow speed devices, valid range is 10-255 #define USB_CONFIG_HID_RX_POLL 10 #endif //Tells the CCS PIC USB firmware to include HID handling code. #ifdef USB_HID_DEVICE #undef USB_HID_DEVICE #endif #DEFINE USB_HID_DEVICE
TRUE
//the following defines needed for the CCS USB PIC driver to enable the TX endpoint 1 // and allocate buffer space on the peripheral
Ingeniero en Automática y Electrónica Industria
41
Fernando Bueno Zambruno #ifdef USB_EP1_TX_ENABLE #undef USB_EP1_TX_ENABLE #endif #define USB_EP1_TX_ENABLE fers
Proyecto Final de Carrera
USB_ENABLE_INTERRUPT
//turn on EP1 for IN bulk/interrupt trans-
#ifndef USB_EP1_TX_SIZE #if (USB_CONFIG_HID_TX_SIZE >= 64) // interrupt endpoint max packet size is 64. #define USB_EP1_TX_SIZE 64 #else // by making EP packet size larger than message size, we can send message in one packet. #define USB_EP1_TX_SIZE (USB_CONFIG_HID_TX_SIZE+1) #endif #endif #ifdef USB_EP1_RX_ENABLE #undef USB_EP1_RX_ENABLE #endif #define USB_EP1_RX_ENABLE transfers
USB_ENABLE_INTERRUPT
//turn on EP1 for OUT bulk/interrupt
#ifndef USB_EP1_RX_SIZE #if (USB_CONFIG_HID_RX_SIZE >= 64) // interrupt endpoint max packet size is 64. #define USB_EP1_RX_SIZE 64 #else // by making EP packet size larger than message size, we can send message in one packet. #define USB_EP1_RX_SIZE (USB_CONFIG_HID_RX_SIZE+1) #endif #endif #include ////////////////////////////////////////////////////////////////// /// /// HID Report. Tells HID driver how to handle and deal with /// received data. HID Reports can be extremely complex, /// see HID specifcation for help on writing your own. /// ////////////////////////////////////////////////////////////////// const char USB_CLASS_SPECIFIC_DESC[] = { 6, 0, 255, // Usage Page = Vendor Defined 9, 1, // Usage = IO device 0xa1, 1, // Collection = Application 0x19, 1, // Usage minimum 0x29, 8, // Usage maximum 0x15, 0x80, 0x25, 0x7F, 0x75, 0x95, 0x81, 0x19, 0x29, 0x75, 0x95, 0x91,
// Logical minimum (-128) // Logical maximum (127)
8, // Report size = 8 (bits) USB_CONFIG_HID_TX_SIZE, // Report count 32 bytes 2, // Input (Data, Var, Abs) 1, // Usage minimum 8, // Usage maximum 8, // Report size = 8 (bits) USB_CONFIG_HID_RX_SIZE, // Report count 32 bytes 2, // Output (Data, Var, Abs)
Ingeniero en Automática y Electrónica Industria
42
Fernando Bueno Zambruno 0xc0
Proyecto Final de Carrera
// End Collection
}; //if a class has an extra descriptor not part of the config descriptor, // this lookup table defines where to look for it in the const // USB_CLASS_SPECIFIC_DESC[] array. //first element is the config number (if your device has more than one config) //second element is which interface number //set element to 0xFFFF if this config/interface combo doesn't exist const int16 USB_CLASS_SPECIFIC_DESC_LOOKUP[USB_NUM_CONFIGURATIONS][1] = { //config 1 //interface 0 0 }; //if a class has an extra descriptor not part of the config descriptor, // this lookup table defines the size of that descriptor. //first element is the config number (if your device has more than one config) //second element is which interface number //set element to 0xFFFF if this config/interface combo doesn't exist const int16 USB_CLASS_SPECIFIC_DESC_LOOKUP_SIZE[USB_NUM_CONFIGURATIONS][1] = { //config 1 //interface 0 32 }; ////////////////////////////////////////////////////////////////// /// /// start config descriptor /// right now we only support one configuration descriptor. /// the config, interface, class, and endpoint goes into this array. /// ////////////////////////////////////////////////////////////////// #DEFINE USB_TOTAL_CONFIG_LEN points)
41
//config+interface+class+endpoint+endpoint (2 end-
const char USB_CONFIG_DESC[] = { //IN ORDER TO COMPLY WITH WINDOWS HOSTS, THE ORDER OF THIS ARRAY MUST BE: // config(s) // interface(s) // class(es) // endpoint(s) //config_descriptor for config index 1 USB_DESC_CONFIG_LEN, //length of descriptor size ==1 USB_DESC_CONFIG_TYPE, //constant CONFIGURATION (CONFIGURATION 0x02) ==2 USB_TOTAL_CONFIG_LEN,0, //size of all data returned for this config ==3,4 1, //number of interfaces this device supports ==5 0x01, //identifier for this configuration. (IF we had more than one configurations) ==6 0x00, //index of string descriptor for this configuration ==7 #if USB_CONFIG_BUS_POWER 0x80, //bit 6=1 if self powered, bit 5=1 if supports remote wakeup (we don't), bits 0-4 unused and bit7=1 ==8 #else 0xC0, //bit 6=1 if self powered, bit 5=1 if supports remote wakeup (we don't), bits 0-4 unused and bit7=1 ==8
Ingeniero en Automática y Electrónica Industria
43
Fernando Bueno Zambruno
Proyecto Final de Carrera
#endif USB_CONFIG_BUS_POWER/2, //maximum bus power required (maximum milliamperes/2) (0x32 = 100mA) //interface descriptor 1 USB_DESC_INTERFACE_LEN, //length of descriptor =10 USB_DESC_INTERFACE_TYPE, //constant INTERFACE (INTERFACE 0x04) =11 0x00, //number defining this interface (IF we had more than one interface) ==12 0x00, //alternate setting ==13 2, //number of endpoins, except 0 (pic167xx has 3, but we dont have to use all). ==14 0x03, //class code, 03 = HID ==15 0x00, //subclass code //boot ==16 0x00, //protocol code ==17 0x00, //index of string descriptor for interface ==18 //class descriptor 1 (HID) USB_DESC_CLASS_LEN, //length of descriptor ==19 USB_DESC_CLASS_TYPE, //dscriptor type (0x21 == HID) ==20 0x00,0x01, //hid class release number (1.0) ==21,22 0x00, //localized country code (0 = none) ==23 0x01, //number of hid class descrptors that follow (1) ==24 0x22, //report descriptor type (0x22 == HID) ==25 USB_CLASS_SPECIFIC_DESC_LOOKUP_SIZE[0][0], 0x00, //length of report descriptor ==26,27 //endpoint descriptor USB_DESC_ENDPOINT_LEN, //length of descriptor ==28 USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (ENDPOINT 0x05) ==29 0x81, //endpoint number and direction (0x81 = EP1 IN) ==30 USB_EP1_TX_ENABLE, //transfer type supported (0x03 is interrupt) ==31 USB_EP1_TX_SIZE,0x00, //maximum packet size supported ==32,33 USB_CONFIG_HID_TX_POLL, //polling interval, in ms. (cant be smaller than 10 for slow speed) ==34 //endpoint descriptor USB_DESC_ENDPOINT_LEN, //length of descriptor ==35 USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (ENDPOINT 0x05) ==36 0x01, //endpoint number and direction (0x01 = EP1 OUT) ==37 USB_EP1_RX_ENABLE, //transfer type supported (0x03 is interrupt) ==38 USB_EP1_RX_SIZE,0x00, //maximum packet size supported ==39,40 USB_CONFIG_HID_RX_POLL //polling interval, in ms. (cant be smaller than 10 for slow speed) ==41 }; //****** BEGIN CONFIG DESCRIPTOR LOOKUP TABLES ******** //since we can't make pointers to constants in certain pic16s, this is an offset table to find // a specific descriptor in the above table. //NOTE: DO TO A LIMITATION OF THE CCS CODE, ALL HID INTERFACES MUST START AT 0 AND BE SEQUENTIAL // FOR EXAMPLE, IF YOU HAVE 2 HID INTERFACES THEY MUST BE INTERFACE 0 AND INTERFACE 1 #define USB_NUM_HID_INTERFACES 1 //the maximum number of interfaces seen on any config //for example, if config 1 has 1 interface and config 2 has 2 interfaces you must define this as 2 #define USB_MAX_NUM_INTERFACES 1
Ingeniero en Automática y Electrónica Industria
44
Fernando Bueno Zambruno
Proyecto Final de Carrera
//define how many interfaces there are per config. [0] is the first config, etc. const char USB_NUM_INTERFACES[USB_NUM_CONFIGURATIONS]={1}; //define where to find class descriptors //first dimension is the config number //second dimension specifies which interface //last dimension specifies which class in this interface to get, but most will only have 1 class per interface //if a class descriptor is not valid, set the value to 0xFFFF const int16 USB_CLASS_DESCRIPTORS[USB_NUM_CONFIGURATIONS][1][1]= { //config 1 //interface 0 //class 1 18 }; #if (sizeof(USB_CONFIG_DESC) != USB_TOTAL_CONFIG_LEN) #error USB_TOTAL_CONFIG_LEN not defined correctly #endif ////////////////////////////////////////////////////////////////// /// /// start device descriptors /// ////////////////////////////////////////////////////////////////// const char USB_DEVICE_DESC[USB_DESC_DEVICE_LEN] ={ //starts of with device configuration. only one possible USB_DESC_DEVICE_LEN, //the length of this report ==1 0x01, //the constant DEVICE (DEVICE 0x01) ==2 0x10,0x01, //usb version in bcd ==3,4 0x00, //class code ==5 0x00, //subclass code ==6 0x00, //protocol code ==7 USB_MAX_EP0_PACKET_LENGTH, //max packet size for endpoint 0. (SLOW SPEED SPECIFIES 8) ==8 USB_CONFIG_VID & 0xFF, ((USB_CONFIG_VID >> 8) & 0xFF), //vendor id ==9, 10 USB_CONFIG_PID & 0xFF, ((USB_CONFIG_PID >> 8) & 0xFF), //product id, don't use 0xffff ==11, 12 USB_CONFIG_VERSION & 0xFF, ((USB_CONFIG_VERSION >> 8) & 0xFF), //device release number ==13,14 0x01, //index of string description of manufacturer. therefore we point to string_1 array (see below) ==15 0x02, //index of string descriptor of the product ==16 0x00, //index of string descriptor of serial number ==17 USB_NUM_CONFIGURATIONS //number of possible configurations ==18 }; ////////////////////////////////////////////////////////////////// /// /// start string descriptors /// String 0 is a special language string, and must be defined. People in U.S.A. can leave this alone. /// /// You must define the length else get_next_string_character() will not see the string /// Current code only supports 10 strings (0 thru 9) /// //////////////////////////////////////////////////////////////////
Ingeniero en Automática y Electrónica Industria
45
Fernando Bueno Zambruno
//the offset of the starting location of each string. offset[1] is the start of string 1, etc. char USB_STRING_DESC_OFFSET[]={0,4,12};
Proyecto Final de Carrera
offset[0] is the start of string 0,
// Here is where the "CCS" Manufacturer string and "CCS HID Demo" are stored. // Strings are saved as unicode. // These strings are mostly only displayed during the add hardware wizard. // Once the operating system drivers have been installed it will usually display // the name from the drivers .INF. char const USB_STRING_DESC[]={ // Primer descriptor. 4, // Longitud del descriptor. USB_DESC_STRING_TYPE, 0x09,0x04, // Lenguaje id = Inglés (Definido por microsoft). // Segundo descriptor. 8, // Longitud del descriptor. USB_DESC_STRING_TYPE, // Descriptor del compilador utilizado. (STRING) (Puede ser el nombre de la compañía) 'C',0, 'C',0, 'S',0, // Tercer descriptor. 32, // Longitud del descriptor. USB_DESC_STRING_TYPE, // Descriptor del fabricante: MoyaPIC_easyHID. (STRING) 'M',0, 'o',0, 'y',0, 'a',0, 'P',0, 'I',0, 'C',0, '_',0, 'e',0, 'a',0, 's',0, 'y',0, 'H',0, 'I',0, 'D',0 }; #ENDIF
Ingeniero en Automática y Electrónica Industria
46
Fernando Bueno Zambruno
Proyecto Final de Carrera
ANEXO 2:Programas de la aplicación del PC Form1.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Diagnostics; using System.Windows.Forms; using System.Runtime.InteropServices; using MecaniqueUK; namespace WindowsFormsApplication1 { public partial class USB_FORM : Form { /***************************************************/ // Variables definidas por el usuario. UInt32 controlador; string ctemp, chumed; char temp1, temp2, temp3, temp4, temp5, temp6; char humed1, humed2, humed3, humed4, humed5, humed6; int salida_t = 0; int salida_h = 0; double ayuda_t, ayuda_h; int i = 0; int j = 0; int k = 0; int l = 0; int pepe_t, pepe_h; int boton_control_temp = 0; //Variable que indica si el control de temperatura está en Manual o Auto. double tempcamp, humedcamp, Prop_t, Derv_t, Inte_t, SP_t; double Er_t, Er_ant_t, Suma_Er_t, Sal_ant_t; double Prop_h, Derv_h, Inte_h, SP_h; double Er_h, Er_ant_h, Suma_Er_h, Sal_ant_h; int boton_control_humed = 0; //Variable que indica si el control de humedad está en Manual o Auto. int periodo_calef = 0; int ciclo_calef = 0; int cuenta_calef = 0; int periodo_humed = 0; int ciclo_humed = 0; int cuenta_humed = 0; /***************************************************/ public USB_FORM() { InitializeComponent(); MENSAJE_TOOL.SetToolTip(this.CONECTAR_DISPOSITIVO, "Botón que enlaza el dispositivo al controlador.");
Ingeniero en Automática y Electrónica Industria
47
Fernando Bueno Zambruno
Proyecto Final de Carrera
} private void FormMain_Load_1(object sender, EventArgs e) { timer1.Enabled = true; deshabilita_controles(); } private void FormMain_FormClosed(object sender, FormClosedEventArgs e) { EasyHID.Disconnect(); } private void Dispositivo_Conectado(UInt32 handle) { if (EasyHID.GetVendorID(handle) == EasyHID.VENDOR_ID && EasyHID.GetProductID(handle) == EasyHID.PRODUCT_ID) { // Si el handler ha encontrado un dispositivo conectado... EasyHID.SetReadNotify(handle, true); // Activa el sistema de notificaciones. controlador = handle; } } private void Dispositivo_desconectado(UInt32 handle) { if (EasyHID.GetVendorID(handle) == EasyHID.VENDOR_ID && EasyHID.GetProductID(handle) == EasyHID.PRODUCT_ID) { CONECTAR_DISPOSITIVO.BackColor = Color.Red; CONECTAR_DISPOSITIVO.ForeColor = Color.White; CONECTAR_DISPOSITIVO.Text = "CONECTAR DISPOSITIVO"; deshabilita_controles(); EasyHID.Disconnect(); MessageBox.Show("PROBLEMAS DE COMUNICACION\n LA APLICACION SE CERRARA ", "Alerta", MessageBoxButtons.OK, MessageBoxIcon .Warning); Close(); } } private void Lee_datos(UInt32 In_handle) { if (CONECTAR_DISPOSITIVO.Text == "DISPOSITIVO CONECTADO ") { byte[] BufferINP = new byte[EasyHID.BUFFER_IN_SIZE]; // Declaramos el buffer de entrada. if ((EasyHID.Read(In_handle, out BufferINP)) == true) // Si hay datos, los procesamos... { temp1 temp2 temp3 temp4 temp5 temp6
= = = = = =
(char)BufferINP[1]; (char)BufferINP[2]; (char)BufferINP[3]; (char)BufferINP[4]; (char)BufferINP[5]; (char)BufferINP[6];
ctemp = temp2.ToString() + temp3.ToString() + ',' + temp5.ToString() + temp6.ToString(); tempcamp = Convert.ToDouble(ctemp); ctemp += " ºC";
Ingeniero en Automática y Electrónica Industria
48
Fernando Bueno Zambruno
Proyecto Final de Carrera
label11.Text = ctemp; humed1 humed2 humed3 humed4 humed5 humed6
= = = = = =
(char)BufferINP[7]; (char)BufferINP[8]; (char)BufferINP[9]; (char)BufferINP[10]; (char)BufferINP[11]; (char)BufferINP[12];
chumed = humed2.ToString() + humed3.ToString() + ',' + humed5.ToString() + humed6.ToString(); humedcamp = Convert.ToDouble(chumed); chumed += " %H"; label12.Text = chumed; } } } protected override void WndProc(ref Message message) { // Interceptamos los mensajes de windows. if (message.Msg == EasyHID.WM_HID_EVENT) // Si ha ocurrido algún evento... { switch (message.WParam.ToInt32()) // Intercepta el mensaje y opera según el valor recibido.... { case EasyHID.NOTIFY_PLUGGED: Dispositivo_Conectado((UInt32)message.LParam.ToInt32()); // Se ha conectado un dispositivo. break; case EasyHID.NOTIFY_UNPLUGGED: Dispositivo_desconectado((UInt32)message.LParam.ToInt32()); // Se ha desconectado un dispositivo. break; case EasyHID.NOTIFY_READ: Lee_datos((UInt32)message.LParam.ToInt32()); // Hay datos en el buffer de entrada. break; } } base.WndProc(ref message); } private void OUT_DIGITAL_1_Click(object sender, EventArgs e) { if (j == 0) { OUT_DIGITAL_1.BackColor = Color.Green; j = 1; } else { OUT_DIGITAL_1.BackColor = Color.Red; j = 0; } byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID
Ingeniero en Automática y Electrónica Industria
49
Fernando Bueno Zambruno
Proyecto Final de Carrera
BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x10; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } private void OUT_DIGITAL_2_Click_1(object sender, EventArgs e) { if (k == 0) { OUT_DIGITAL_2.BackColor = Color.Green; k = 1; } else { OUT_DIGITAL_2.BackColor = Color.Red; k = 0; } byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x20; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } private void B_Cont_temp_Click(object sender, EventArgs e) { if (boton_control_temp == 0) { B_Cont_temp.Text = "MANUAL"; Ind_cont_temp.BackColor = Color.Green; Ind_cont_temp.Text = "CONTROL AUTO"; boton_control_temp = 1; numericUpDown1.Enabled = false; REF1.Enabled = true; SP_t = tempcamp; label23.Text = string.Format("{0:F2}", SP_t); Er_t = 0; Er_ant_t = 0; Suma_Er_t = 0; Sal_ant_t= salida_t; } else { B_Cont_temp.Text = "AUTO"; Ind_cont_temp.BackColor = Color.Red; Ind_cont_temp.Text = "CONTROL MANUAL"; Sal_ant_t = ayuda_t; pepe_t = Convert.ToInt16(ayuda_t / 2.46); label22.Text = System.Convert.ToString(pepe_t) + '%'; numericUpDown1.Enabled = true; numericUpDown1.Value = Convert.ToDecimal(pepe_t); boton_control_temp = 0; REF1.Enabled = false;
Ingeniero en Automática y Electrónica Industria
50
Fernando Bueno Zambruno
Proyecto Final de Carrera
} } private void numericUpDown1_ValueChanged(object sender, EventArgs e) { pepe_t = Convert.ToInt16( numericUpDown1.Value); label22.Text = Convert.ToString(numericUpDown1.Value) + '%'; ayuda_t = pepe_t * 2.46; salida_t = Convert.ToInt16(ayuda_t); byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0B; // Primero enviamos un comando de control al dispositivo: 0x0B (COMANDO_PWM1). BufferOUT[3] = Convert.ToByte(salida_t); // Luego enviamos los datos del duty_cicle del PWM1. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } private void textBox2_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == Convert.ToChar(13)) { // Se pulsó la tecla Entrar e.Handled = true; if (i == 1) { SP_t = Convert.ToDouble(REF1.Text); label23.Text = string.Format("{0:F2}", SP_t); REF1.Text = ""; } if (i == 2) { Prop_t = Convert.ToDouble(kp1.Text); labelkp.Text = string.Format("{0:F5}", Prop_t); kp1.Text = ""; } if (i == 3) { Derv_t = Convert.ToDouble(kd1.Text); labelkd.Text = string.Format("{0:F5}", Derv_t); kd1.Text = ""; } if (i == 4) { Inte_t = Convert.ToDouble(ki1.Text); labelki.Text = string.Format("{0:F5}", Inte_t); ki1.Text = ""; } if (i == 5) { SP_h = Convert.ToDouble(REF2.Text); labelREF2.Text = string.Format("{0:F2}", SP_h); REF2.Text = ""; } if (i == 6) {
Ingeniero en Automática y Electrónica Industria
51
Fernando Bueno Zambruno
Proyecto Final de Carrera
Prop_h = Convert.ToDouble(kp2.Text); labelkp2.Text = string.Format("{0:F5}", Prop_h); kp2.Text = ""; } if (i == 7) { Derv_h = Convert.ToDouble(kd2.Text); labelkd2.Text = string.Format("{0:F5}", Derv_h); kd2.Text = ""; } if (i == 8) { Inte_h = Convert.ToDouble(ki2.Text); labelki2.Text = string.Format("{0:F5}", Inte_h); ki2.Text = ""; } } else if (e.KeyChar == Convert.ToChar(8)) { // Se pulsó la tecla retroceso e.Handled = false; } else if (e.KeyChar == ',') { TextBox ObjTextBox = (TextBox)sender; if (ObjTextBox.Text.IndexOf(',') != -1) { // Sólo puede haber una coma e.Handled = true; } } else if (e.KeyChar == '-' || e.KeyChar == '+') { TextBox ObjTextBox = (TextBox)sender; // Admitir - o + sólo en la primera posición: if (ObjTextBox.SelectionLength == ObjTextBox.TextLength) { // Todo el texto está seleccionado: se sobrescribe con el signo e.Handled = false; } else if (ObjTextBox.TextLength != 0) { // La primera posición ya está ocupada e.Handled = true; } } else if (e.KeyChar < '0' || e.KeyChar > '9') { // Desechar los caracteres que no son dígitos e.Handled = true; } } private void CONECTAR_DISPOSITIVO_Click(object sender, EventArgs e) { EasyHID.Connect(Handle); if (EasyHID.IsAvailable(EasyHID.VENDOR_ID, EasyHID.PRODUCT_ID) == true)
Ingeniero en Automática y Electrónica Industria
52
Fernando Bueno Zambruno { //Si
Proyecto Final de Carrera
el dispositivo está conectado...
habilita_controles(); CONECTAR_DISPOSITIVO.Text = "DISPOSITIVO CONECTADO "; CONECTAR_DISPOSITIVO.BackColor = Color.GreenYellow; CONECTAR_DISPOSITIVO.ForeColor = Color.Black; } else { CONECTAR_DISPOSITIVO.Text = "CONTROLADOR CONECTADO "; CONECTAR_DISPOSITIVO.BackColor = Color.Blue; CONECTAR_DISPOSITIVO.ForeColor = Color.White; } } private void timer1_Tick(object sender, EventArgs e) { statusStrip1.Items[0].Text = DateTime.Now.ToLongTimeString(); if (boton_control_temp == 1) { Er_t = SP_t - tempcamp; //Error ayuda_t = Sal_ant_t + (Prop_t * Er_t) + (Derv_t * (Er_t - Er_ant_t)) + (Inte_t * Er_t + Suma_Er_t); // Calculo de la salida Suma_Er_t = Inte_t * Er_t + Suma_Er_t; //Actualizacion del termino integral Er_ant_t = Er_t; //Actualización de error anterior if (ayuda_t > { ayuda_t = Suma_Er_t } if (ayuda_t < { ayuda_t = Suma_Er_t }
246) 246; = 0; 0) 0; = 0;
salida_t = Convert.ToInt16(ayuda_t); byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0B; // Primero enviamos un comando de control al dispositivo: 0x0B (COMANDO_PWM1). BufferOUT[3] = Convert.ToByte(salida_t); // Luego enviamos los datos del duty_cicle del PWM1. EasyHID.Write(controlador, BufferOUT); // Envía los datos. Sal_ant_t = ayuda_t; pepe_t = Convert.ToInt16(ayuda_t / 2.46); label22.Text = System.Convert.ToString(pepe_t) + '%'; } if (boton_control_humed == 1) {
Ingeniero en Automática y Electrónica Industria
53
Fernando Bueno Zambruno
Proyecto Final de Carrera
Er_h = SP_h - humedcamp; //Error ayuda_h = Sal_ant_h + ( Prop_h * Er_h) + (Derv_h * (Er_h - Er_ant_h)) + (Inte_h * Er_h + Suma_Er_h);// Calculo de la salida Suma_Er_h = Inte_h * Er_h + Suma_Er_h; //Actualizacion del termino integral Er_ant_h = Er_h; //Actualización de error anterior if (ayuda_h > 246) { ayuda_h = 246; Suma_Er_h = 0; } if (ayuda_h < 0) { ayuda_h = 0; Suma_Er_h = 0; } salida_h = Convert.ToInt16(ayuda_h); byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0C; // Primero enviamos un comando de control al dispositivo: 0x0C (COMANDO_PWM2). BufferOUT[4] = Convert.ToByte(salida_h); // Luego enviamos los datos del duty_cicle del PWM2. EasyHID.Write(controlador, BufferOUT); // Envía los datos. Sal_ant_h = ayuda_h; pepe_h = Convert.ToInt16(ayuda_h / 2.46); label24.Text = System.Convert.ToString(pepe_h) + '%'; } if (button2.Text == "CONTROL AUTO") { if (periodo_calef > 0) { if (ciclo_calef > 0) { if (cuenta_calef == 0) { if (j == 0) { OUT_DIGITAL_1.BackColor = Color.Green; j = 1; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: // 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x10; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } } if ((cuenta_calef >= ciclo_calef)&&(j==1)) { OUT_DIGITAL_1.BackColor = Color.Red;
Ingeniero en Automática y Electrónica Industria
54
Fernando Bueno Zambruno
Proyecto Final de Carrera j = 0; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control
al dispositivo: // 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x10; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } cuenta_calef++; //incrementa contador label10.Text = string.Format("{0:D}", cuenta_calef) + " sg"; if (cuenta_calef >= periodo_calef) { cuenta_calef = 0; //reinicia contador label10.Text = string.Format("{0:D}", cuenta_calef) + " sg"; } } } } if (button3.Text == "CONTROL AUTO") { if (periodo_humed > 0) { if (ciclo_humed > 0) { if (cuenta_humed == 0) { if (k == 0) { OUT_DIGITAL_2.BackColor = Color.Green; k = 1; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: 0x0A (COMANDO_SALIDAS) BufferOUT[2] = 0x20; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } } if ((cuenta_humed >= ciclo_humed) && (k == 1)) { OUT_DIGITAL_2.BackColor = Color.Red; k = 0; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x20; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos.
Ingeniero en Automática y Electrónica Industria
55
Fernando Bueno Zambruno
Proyecto Final de Carrera
} cuenta_humed++; //incrementa contador label17.Text = string.Format("{0:D}", cuenta_humed) + " sg"; if (cuenta_humed >= periodo_humed) { cuenta_humed = 0; //reinicia contador label17.Text = string.Format("{0:D}", cuenta_humed) + " sg"; } } } } } private void salirToolStripMenuItem_Click(object sender, EventArgs e) { EasyHID.Disconnect(); this.Close(); } private void habilita_controles() { // Habilita salidas digitales. Control_Calefactor.Enabled = true; Control_Humidificador.Enabled = true; Control_Temperatura.Enabled = true; Control_Humedad.Enabled = true; } private void deshabilita_controles() { // deshabilita salidas digitales. Control_Calefactor.Enabled = false; Control_Humidificador.Enabled = false; Control_Temperatura.Enabled = false; Control_Humedad.Enabled = false; } private void REF1_Enter(object sender, EventArgs e) { i = 1; } private void kp1_Enter(object sender, EventArgs e) { i = 2; } private void kd1_Enter(object sender, EventArgs e) { i = 3; } private void ki1_Enter(object sender, EventArgs e) { i = 4; }
Ingeniero en Automática y Electrónica Industria
56
Fernando Bueno Zambruno
Proyecto Final de Carrera
private void REF2_Enter(object sender, EventArgs e) { i = 5; } private void kp2_Enter(object sender, EventArgs e) { i = 6; } private void kd2_Enter(object sender, EventArgs e) { i = 7; } private void ki2_Enter(object sender, EventArgs e) { i = 8; } private void B_Cont_humed_Click(object sender, EventArgs e) { if (boton_control_humed == 0) { B_Cont_humed.Text = "MANUAL"; Ind_cont_humed.BackColor = Color.Green; Ind_cont_humed.Text = "CONTROL AUTO"; boton_control_humed = 1; numericUpDown2.Enabled = false; REF2.Enabled = true; SP_h = humedcamp; labelREF2.Text = string.Format("{0:F2}", SP_h); Er_h = 0; Er_ant_h = 0; Suma_Er_h = 0; Sal_ant_h = salida_h; } else { B_Cont_humed.Text = "AUTO"; Ind_cont_humed.BackColor = Color.Red; Ind_cont_humed.Text = "CONTROL MANUAL"; Sal_ant_h = ayuda_h; pepe_h = Convert.ToInt16(ayuda_h / 2.46); label24.Text = System.Convert.ToString(pepe_h) + '%'; numericUpDown2.Enabled = true; numericUpDown2.Value = Convert.ToDecimal(pepe_h); boton_control_humed = 0; REF2.Enabled = false; } } private void numericUpDown2_ValueChanged(object sender, EventArgs e) { pepe_h = Convert.ToInt16(numericUpDown2.Value); label24.Text = Convert.ToString(numericUpDown2.Value) + '%';
Ingeniero en Automática y Electrónica Industria
57
Fernando Bueno Zambruno
Proyecto Final de Carrera
ayuda_h = pepe_h * 2.46; salida_h = Convert.ToInt16(ayuda_h); byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0C; // Primero enviamos un comando de control al dispositivo: 0x0C (COMANDO_PWM2). BufferOUT[4] = Convert.ToByte(salida_h); // Luego enviamos los datos del duty_cicle del PWM2. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == Convert.ToChar(13)) { // Se pulsó la tecla Entrar e.Handled = true; if (l == 1) { periodo_calef = Convert.ToInt16(textBox1.Text); label6.Text = string.Format("{0:D}", periodo_calef) + " sg"; textBox1.Text = ""; } if (l == 2) { if (periodo_calef { ciclo_calef = label8.Text = textBox2.Text }
>= Convert.ToInt16(textBox2.Text)) Convert.ToInt16(textBox2.Text); string.Format("{0:D}", ciclo_calef) + " sg"; = "";
} if (l == 3) { periodo_humed = Convert.ToInt16(textBox3.Text); label27.Text = string.Format("{0:D}", periodo_humed) + " sg"; textBox3.Text = ""; } if (l == 4) { if (periodo_humed >= Convert.ToInt16(textBox4.Text)) { ciclo_humed = Convert.ToInt16(textBox4.Text); label26.Text = string.Format("{0:D}", ciclo_humed) + " sg"; textBox4.Text = ""; } }
Ingeniero en Automática y Electrónica Industria
58
Fernando Bueno Zambruno
Proyecto Final de Carrera
} else if (e.KeyChar == Convert.ToChar(8)) { // Se pulsó la tecla retroceso e.Handled = false; } else if (e.KeyChar < '0' || e.KeyChar > '9') { // Desechar los caracteres que no son dígitos e.Handled = true; } } private void button2_Click(object sender, EventArgs e) { if (button2.Text == "CONTROL MANUAL") { button2.Text = "CONTROL AUTO"; button2.BackColor = Color.LightGreen; cuenta_calef = 0; label10.Text = string.Format("{0:D}", cuenta_calef) + " sg"; if (j == 1) { OUT_DIGITAL_1.BackColor = Color.Red; j = 0; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: // 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x10; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } OUT_DIGITAL_1.Enabled = false; } else { button2.Text = "CONTROL MANUAL"; button2.BackColor = Color.Tomato; OUT_DIGITAL_1.Enabled = true; } } private void textBox1_Enter(object sender, EventArgs e) { l = 1; } private void textBox2_Enter(object sender, EventArgs e) { l = 2; }
Ingeniero en Automática y Electrónica Industria
59
Fernando Bueno Zambruno
Proyecto Final de Carrera
private void button3_Click(object sender, EventArgs e) { if (button3.Text == "CONTROL MANUAL") { button3.Text = "CONTROL AUTO"; button3.BackColor = Color.LightGreen; cuenta_humed = 0; label10.Text = string.Format("{0:D}", cuenta_humed) + " sg"; if (k == 1) { OUT_DIGITAL_1.BackColor = Color.Red; k = 0; byte[] BufferOUT = new byte[EasyHID.BUFFER_OUT_SIZE]; BufferOUT[0] = 0; // Report ID BufferOUT[1] = 0x0A; // Primero enviamos un comando de control al dispositivo: // 0x0A (COMANDO_SALIDAS). BufferOUT[2] = 0x20; // Luego enviamos los datos de control de los LED'S. EasyHID.Write(controlador, BufferOUT); // Envía los datos. } OUT_DIGITAL_2.Enabled = false; } else { button3.Text = "CONTROL MANUAL"; button3.BackColor = Color.Tomato; OUT_DIGITAL_2.Enabled = true; } } private void textBox3_Enter(object sender, EventArgs e) { l = 3; } private void textBox4_Enter(object sender, EventArgs e) { l = 4; }
} }
Ingeniero en Automática y Electrónica Industria
60
Fernando Bueno Zambruno
Proyecto Final de Carrera
EasyHID.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.ComponentModel; using System.Runtime.InteropServices; namespace MecaniqueUK { class EasyHID { // HID specific... public const UInt32 VENDOR_ID = 6017; public const UInt32 PRODUCT_ID = 2000; public const int BUFFER_IN_SIZE = 32; public const int BUFFER_OUT_SIZE = 32; // HID events... private const int WM_APP = 0x8000; public const int WM_HID_EVENT = WM_APP + 200; public const int NOTIFY_PLUGGED = 0x0001; public const int NOTIFY_UNPLUGGED = 0x0002; public const int NOTIFY_CHANGED = 0x0003; public const int NOTIFY_READ = 0x0004; // HID interface... [DllImport("mcHID.dll")] public static extern bool Connect(IntPtr pHostWin); [DllImport("mcHID.dll")] public static extern bool Disconnect(); [DllImport("mcHID.dll")] public static extern UInt32 GetItem(UInt32 pIndex); [DllImport("mcHID.dll")] public static extern UInt32 GetItemCount(); [DllImport("mcHID.dll")] public static extern bool Read(UInt32 pHandle, IntPtr pData); [DllImport("mcHID.dll")] private static extern bool Write(UInt32 pHandle, IntPtr pData); [DllImport("mcHID.dll")] private static extern bool ReadEx(UInt32 pVendorId, UInt32 pProductId, IntPtr pData); [DllImport("mcHID.dll")] private static extern bool WriteEx(UInt32 pVendorId, UInt32 pProductId, IntPtr pData); [DllImport("mcHID.dll")] public static extern UInt32 GetHandle(UInt32 pVendorID, UInt32 pProductId); [DllImport("mcHID.dll")] public static extern UInt32 GetVendorID(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern UInt32 GetProductID(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern UInt32 GetVersionID(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern UInt32 GetInputReportLength(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern UInt32 GetOutputReportLength(UInt32 pHandle); [DllImport("mcHID.dll")]
Ingeniero en Automática y Electrónica Industria
61
Fernando Bueno Zambruno
Proyecto Final de Carrera
public static extern void SetReadNotify(UInt32 pHandle, bool pValue); [DllImport("mcHID.dll")] public static extern bool IsReadNotifyEnabled(UInt32 pHandle); [DllImport("mcHID.dll")] public static extern bool IsAvailable(UInt32 pVendorId, UInt32 pProductId); // Managed version of the read/write functions. public static bool Read(UInt32 pHandle, out byte[] pData) { IntPtr unmanagedBuffer = Marshal.AllocHGlobal(BUFFER_IN_SIZE); bool result = Read(pHandle, unmanagedBuffer); try { pData = new byte[BUFFER_IN_SIZE]; Marshal.Copy(unmanagedBuffer, pData, 0, BUFFER_IN_SIZE); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } return result; } public static bool Write(UInt32 pHandle, byte[] pData) { IntPtr unmanagedBuffer = Marshal.AllocHGlobal(BUFFER_OUT_SIZE); bool result; try { Marshal.Copy(pData, 0, unmanagedBuffer, BUFFER_OUT_SIZE); result = Write(pHandle, unmanagedBuffer); } finally { Marshal.FreeHGlobal(unmanagedBuffer); } return result; } } }
Ingeniero en Automática y Electrónica Industria
62
Fernando Bueno Zambruno
Proyecto Final de Carrera
ANEXO 3: Bibliotecas de CCS CCS proporciona bibliotecas para la conexión de un PIC al PC mediante USB mediante el uso de un PIC con un periférico USB interno (como el PIC16C765 o la familia PIC18F4550) o mediante el uso de cualquier PIC con un periférico externo USB (el Nacional USBN9603 familia). Funciones relevantes:
usb_init () Inicializa el hardware USB. Entonces va a esperar en un bucle infinito para el periférico USB para ser conectado al bus (pero eso no significa que haya sido enumerados por el PC). Permitirá el uso y la interrupción del USB.
usb_init_cs () Al igual que usb_init (), pero no espera a que el dispositivo se conecta al bus. Esto es útil si el dispositivo no está bus y puede funcionar sin una conexión USB.
usb_task () Si usa el sentido de conexión, y el usb_init_cs () para la inicialización, entonces periódicamente debe llamar a esta función para mantener un ojo en el pasador de conexión de sentido. Cuando el PIC está conectado al bus, esta función entonces perpare el periférico USB. Cuando el PIC está desconectado del bus, se restablecerá la pila USB y periféricos. Permitirá el uso y la interrupción del USB.
Nota: En su solicitud, usted debe definir USB_CON_SENSE_PIN a la clavija de conexión de sentido.
usb_detach () Elimina el PIC desde el autobús. Se llamará automáticamente usb_task () si se pierde la conexión, pero puede ser llamado de forma manual por el usuario.
usb_attach () Concede al PIC al bus. Se llamará automáticamente usb_task () si la conexión, pero puede ser llamado de forma manual por el usuario.
usb_attached () Si se utiliza pines sentido (USB_CON_SENSE_PIN), devuelve TRUE si que pin es alto. Persona siempre devuelve TRUE.
Ingeniero en Automática y Electrónica Industria
63
Fernando Bueno Zambruno
Proyecto Final de Carrera
usb_enumerated () Devuelve TRUE si el dispositivo ha sido enumerado por el PC. Si el dispositivo ha sido enumerado por el PC, eso significa que está en el modo de funcionamiento normal y se puede enviar / recibir paquetes.
usb_put_packet (Punto final, los datos, len, TGL) Coloca el paquete de datos en el búfer de punto final especificado. Devuelve TRUE si éxito, FALSE si el buffer está todavía llena con el último paquete.
usb_puts (Punto final, los datos, len, tiempo de espera) Envía los siguientes datos para el punto final especificado. usb_puts () difiere de usb_put_packet () en el que va a enviar mensajes de múltiples paquetes, si los datos no caben en un paquete.
usb_kbhit (punto final) Devuelve TRUE si el punto final especificado en los datos es el búfer de recepción.
usb_get_packet (Variable, ptr, max) Lee hasta un máximo de bytes del búfer de extremo especificado y lo guarda en el ptr puntero. Devuelve el número de bytes guardados en ptr.
usb_gets (variable, ptr, máximo, tiempo de espera) Lee un mensaje desde el punto final especificado. La diferencia usb_get_packet () y usb_gets () es que usb_gets () esperará hasta que un mensaje completo ha recibido, que un mensaje puede contener más de un paquete. Devuelve el número de los bytes recibidos.
Ingeniero en Automática y Electrónica Industria
64
Fernando Bueno Zambruno
Proyecto Final de de Carrera
Archivos relevantes:
pic_usb.h Hardware controlador de capa de la familia de controladores PIC PIC16C765 con un periférico USB interno.
pic_18usb.h Hardware controlador de capa de la familia PIC18F4550 controladores PIC con un periférico USB interno.
usbn960x.h Controladores de hardware para la capa externa Nacional USBN9603/USBN9604 USBN9603/USBN9604 periféricos USB. Usted puede usar este periférico externo USB para añadir a cualquier microcontrolador. microcontrolador.
usb.h Definiciones comunes y prototipos utilizados por el controlador USB usb.c La pila USB, que se ocupa de la interrupción USB y USB solicitudes solicitudes de instalación de punto final 0.
usb_cdc.h Un conductor que lleva el anterior son los archivos para crear un dispositivo USB CDC, que emula un dispositivo RS232 legado y se muestra como un puerto COM en el Administrador de dispositivos de MS Windows.
Ingeniero en Automática y Electrónica Industria Industria
65
Fernando Bueno Zambruno
Proyecto Final de de Carrera
ANEXO 4:
USB
Firmware para dispositivo esclavo USB de Clase HID Emanuel G. Aguirre, Pablo A. Di Giulio Uni ver ver sid si dad T ecnológi cnológica ca N acio ci onal, F acultad cultad R eg i onal nal Sa S an F r ancisco nci sco
1.
Introducción
El Bus USB por Universal Serial Bus (en adelante USB), fue ideado para ser un bus de extensión para PC, con el objetivo de llegar a ser un estándar de la industria. Las posibles tasas de transferencia son: 1,5 Mbits/s, 12 Mbits/s y 480 Mbits/s, lo cual lo hace útil para aplicaciones que van desde periféricos de PC hasta dispositivos de video u otros que requieran alta tasa de transferencia de datos. Otro motivo fue la creación de un bus que fuera independiente de la plataforma de hardware utilizada, al hacer que gran parte del funcionamiento dependa del software. Otro motivo es la creación de una arquitectura de bus que permitiera conectar los periféricos de la PC con un mismo conector y que además fuera Hot Plug And Play, haciéndolo de esta forma más simple para el usuario; evitando así la multiplicidad de conectores tales como el Puerto Serial, Puerto Paralelo, PS/2, Gameport, y demás derivados del diseño original de la PC de IBM de los años 80. Aún otro motivo es la reducción de costo. Dándose ésta en la reducción de cables de cobre (por ser un bus bus de tipo serial), en la eliminación eliminación de la amplia amplia variedad de conectores (siendo todos reemplazados por un solo tipo). La alternativa actual al USB es el bus FireWire (IEEE-1394), creado por Apple Computer. El bus IEEE-1394 es más rápido y más flexible que el USB, pero es más caro. La ventaja del USB USB es que es más útil útil para periféricos de baja baja tasa de transferencia de datos, tales como periféricos de PC. Mientras que en el USB el Host es el que controla las comunicaciones, comunicaciones, el IEEE-1394 utiliza el modelo peer-to-peer en el cual los periféricos pueden comunicarse entre sí. El IEEE-1394 tiene una taza de transferencia transferencia de 400 Mbits/s. Los 480 Mbits/s del USB 2.0 full-speed son actualmente superados por el IEEE-1394b que llega a los 3,6 Gb/s. La motivación principal para la realización de este trabajo fue profundizar en esta tecnología, que actualmente es muy usada en el mundo, pero que en Argentina se utiliza en forma escasa. El objetivo del trabajo fue obtener el conocimiento y las aptitudes suficientes suficientes para lograr la utilización del bus USB como interfaz con una PC para aplicaciones de uso general. Luego se procedió a realizar la comunicación con el Driver del Host (PC) de la Ingeniero en Automática y Electrónica Industria Industria
66
Fernando Bueno Zambruno
Proyecto Final de de Carrera
Clase HID del sistema operativo. El Driver del Host de la Clase HID está disponible en todos los sistemas operativos de uso común en PCs. La Clase HID, es una subclase de dispositivos dispositivos USB cuya función es proveer a la PC de una interfaz con el usuario (mouse, teclado, joystick, etc.). Está especificada como un estándar, definido por el USB Implementers Forum y su versión actual es la HID 1.11. 2.
Elementos del Trabajo y metodología
Los elementos utilizados para el presente trabajo fueron los siguientes: -Placa de desarrollo con un Microcontrolador (MCU) Freescale Freescale 68HC908JB8 (Hardware). -Freescale CodeWarrior Development Studio v5.7 para MCUs 68HC(S)908 (Compilador). -Snoopy Pro v0.22 para Windows XP (USB Sniffer). -Perisoft Bus Hound v6.0 para Windows XP (USB Sniffer). En la arquitectura USB las capas de hardware y de software tienen a cargo distintas funciones, tanto en el Esclavo como en el Host. El MCU 68HC908JB8 se usó para implementar la capa de hardware del Esclavo USB. El IDE CodeWarrior se utilizó para programar la capa de software del Esclavo USB, simularla, y para grabarla en la memoria Flash del MCU. Los USB Sniffers se utilizaron para analizar las estructuras de datos del sistema operativo que reflejan el tráfico en el bus USB y así depurar el Firmware del MCU. 2.1.
Arquitectura Arquitectura del bus USB
Para lograr un mínimo grado de comprensión del presente trabajo, es necesario analizar los elementos del bus USB desde el punto de vista de los sistemas de comunicaciones. comunicaciones. 2.1.1. Elementos del USB
El USB es un bus ideado para intercambio de datos entre un computador anfitrión (Host), y dispositivos conectados a él (Esclavos). Los periféricos conectados conectados al USB comparten el ancho de banda del bus mediante un protocolo basado en mensajes (tokens). Un sistema USB consta de 3 partes: - Anfitrión USB (USB Host o Host). - Dispositivos USB (USB devices). Ingeniero en Automática y Electrónica Industria Industria
67
Fernando Bueno Zambruno
Proyecto Final de Carrera
- Interconexión USB (USB interconnect). Existe sólo un Host en un sistema USB. Los dispositivos USB proveen servicios o funciones al Host. La interconexión USB es la que soporta el tráfico entre el Host y los dispositivos USB, es el canal da comunicación. 2.1.2. Topología
La topología física del USB es de tipo estrella jerarquizada. Con un máximo de 7 niveles de jerarquía.
La topología lógica del USB es de tipo estrella. Lo que implica que en un dispositivo físico puede haber implementado más de un dispositivo lógico (por ej.: un teclado con un mouse incluído).
2.1.3. Direccionamiento
El direccionamiento de dispositivos Esclavos USB se hace mediante un número de 7 bits, lo que hace posible direccionar hasta 128 dispositivos. En el caso de utilizar HUBs, en teoría, pueden conectarse hasta 127 dispositivos USB por HUB. Ingeniero en Automática y Electrónica Industria
68
Fernando Bueno Zambruno
Proyecto Final de Carrera
Dentro de cada dispositivo lógico USB existen uno o más Endpoints, que son receptores independientes de datos. El ruteo de los datos es de tipo Broadcast. Esto es, todos los esclavos dentro de una jerarquía reciben los datos, pero solo los procesa el Esclavo al que están destinados dichos datos. 2.1.4. Acceso al canal de comunicación
El protocolo de acceso al canal de comunicación (media access protocol) es de tipo polled. Por lo tanto, cuando el Host se comunica con un Esclavo, lo hace mediante el envío de un token con la dirección de dicho Esclavo al canal de comunicación (bus), solo el Esclavo con esa dirección procesa el pedido del Host. 2.1.5. Tasas de transferencia de datos
Las posibles tasas de transferencia de datos para el USB son: 1,5 Mbits/s, 12 Mbits/s y 480 Mbits/s. La correspondiente denominación de cada una de ellas es: - low-speed: 1,5 Mbits/s, - full-speed: 12 Mbits/s, - high-speed: 480 Mbits/s. Cada una de las tasas de transferencia anteriores tiene ciertas características propias de funcionamiento y configuración. 2.1.6. Transmisión y codificación
Los datos son transmitidos en forma serie, en 2 líneas de datos complementarias denominadas D+ y D-. Además se proveen 2 líneas de alimentación y de masa respectivamente, las cuales pueden servir para que el dispositivo tome alimentación del Host (5 V, 500 mA máx.). Para transmitir los datos en forma serie se utiliza la codificación Non-ReturnTo-Zero-Inverted o NRZI. En este tipo de codificación, un 0 (cero) se representa sin un cambio de nivel en la tensión, y un 1 (uno) se representa con un cambio de nivel en la tensión. Conjuntamente, se utiliza el bit stuffing, técnica que consiste en insertar un 0 (cero) cada 6 (seis) 1s (unos) consecutivos en el flujo de bits. Además, del bit stuffing y de la codificación NRZI, se utilizan CRCs. Los CRCs se generan después del bit stuffing. 2.2. Protocolo USB
Ingeniero en Automática y Electrónica Industria
69
Fernando Bueno Zambruno
Proyecto Final de Carrera
En el USB, lo datos se envían en paquetes. A su vez, los Paquetes se agrupan para formar las Transacciones, y las Transacciones se agrupan para formar las Transferencias. Las Transferencias son las estructuras de datos que tienen sentido para el Software Client que corre en el Host, y que es el destinatario final de los datos enviados a o recibidos desde el dispositivo lógico. 2.2.1. Endpoints, Buffers y Pipes
Cada canal lógico de comunicación entre el Host y el Esclavo está formado por una tríada Endpoint-Pipe-Buffer. El Endpoint pertenece al Esclavo USB, el Buffer pertenece al Host, y el Pipe es la conexión lógica entre ambos. Existen 2 tipos básicos de Endpoints: Los de Control y los de Datos. Los de Control son utilizados para transferir información de configuración y estado entre Esclavo y el Host. Los de Datos son utilizados para transferir datos que utiliza el software que corre en el Host. Cada Endpoint Está identificado por un número. Los Endpoints de Control pueden transferir datos en ambas direcciones. La clasificación de los Endpoints de datos según la dirección en la que transfieren los datos es: - IN: Los datos van del Esclavo al Host. - OUT: Los datos van del Host al Esclavo. Los Endpoints se agrupan para formar Interfaces. Una Interface representa a un dispositivo lógico USB.
Cada tipo de Endpoint está asociado a un tipo de Transferencia.
Ingeniero en Automática y Electrónica Industria
70
Fernando Bueno Zambruno
Proyecto Final de Carrera
2.2.2. Transferencias
Una Transferencia es un bloque de datos que conforma una estructura comprensible para el Host o el Esclavo. Existen 2 tipos básicos de Transferencias, que estén directamente relacionadas a los tipos de Endpoints: las de Control y las de Datos. Dentro de las Transferencias de datos existen 3 tipos: - Transferencias de Interrupción (Interrupt Data Transfers): Baja tasa de transferencia de datos, es una reliable data transfer. - Transferencias de Bultos (Bulk Data Transfers): Para transferir cantidades relativamente grandes de datos en un solo envío. El ancho de banda disponible para este tipo de transferencia varía en función de la disponibilidad de éste. - Transferencias Isócronas (Isochronous Data Transfers): Ocupan un ancho de banda del USB, cuya latencia es negociada en la configuración. Sirven para transmitir datos a intervalos regulares de tiempo, es una unreliable data transfer. Cada Endpoint de un dispositivo USB está asociado a un y sólo un tipo de Transferencia. El Endpoint0 siempre realiza Control Transfers y todo los Esclavos USB lo tienen. A la Pipe asociada al Endpoint0 se la denomina Default Control Pipe. El Host obtiene la información sobre el tipo de transferencia que realizará cada Endpoint durante el proceso de enumeración, cuando lee los Descriptores. Las Transferencias de Control siempre inician con un paquete SETUP, el cual tiene información sobre la función de configuración que el Host desee que el Esclavo realice. Las Transferencias de datos siempre comienzan con paquetes IN u OUT. 2.3. Comportamiento de Esclavos USB
Un Esclavo USB tiene una cantidad finita de estados posibles.
Ingeniero en Automática y Electrónica Industria
71
Fernando Bueno Zambruno
Proyecto Final de Carrera
A continuación se describe cada uno de los posibles estados de un Esclavo USB: - ATTACHED: Es el primer estado en que se encuentra el Esclavo apenas es conectado físicamente al bus USB. - POWERED: Se considera que un Esclavo USB está en este estado, cuando se ha aplicado Vbus al dispositivo, o cuando se ha alimentado con una fuente externa. - DEFAULT: Un Esclavo USB está en este estado después de que ha recibido una señal de Reset USB. Después de resetearse, éste puede comunicarse con el Host mediante el Endpoint0. Cuando el proceso de reset ha sido completado con éxito, el dispositivo es capaz de comunicarse a la velocidad correcta. La selección para low-speed y full-speed se hace con resistores de terminación. Después de haber sido reseteado, el dispositivo debe ser capaz de responder correctamente y devolver al host los Device Descriptor y Configuration Descriptor, que contienen la información sobre el Esclavo. - ADDRESSED: Un Esclavo USB se encuentra en este estado una vez que le ha sido asignada una dirección USB por el Host. Ingeniero en Automática y Electrónica Industria
72
Fernando Bueno Zambruno
Proyecto Final de Carrera
- CONFIGURED: Antes de que el Esclavo USB pueda prestar su utilidad, éste debe se configurado. Desde el punto de vista del dispositivo, éste estará configurado una vez que haya podido procesar correctamente un Request del tipo SetConfiguration(). - SUSPENDED: Este estado es usado para ahorrar energía. Un Esclavo entra en este estado cuando no ha recibido un paquete EOP durante 3 ms. Un dispositivo debe salir de este estado cuando detecta actividad en el bus. Durante este estado se mantiene la dirección USB que fue asignada al dispositivo USB. 2.3.1. Enumeración
Se denomina Enumeración (bus enumeration), al proceso, por el cual el Host identifica a un dispositivo USB, leyendo sus Descriptores; y luego le asigna una dirección USB. Además de detectar cuando un dispositivo es removido del bus, liberar la dirección USB que este tenía asignada, y liberar los demás procesos asociados a las Pipes creadas para comunicarse con dicho dispositivo. 2.3.2. USB Device Requests
Todos los dispositivos USB responden a los Requests del Host mediante la Default Control Pipe. Los Requests se manifiestan en las llamadas Control Transfers (Transferencias de Control). Una Transferencia de Control se reconoce porque comienza con un paquete de tipo SETUP; a diferencia de los otros tipos de Transferencias que comienzan siempre con paquetes tipo IN u OUT. Los Requests se dividen en: - Standard Device Requests: Son los comunes a todos los tipos de dispositivos USB. Sirven para que el Host identifique y configure un dispositivo durante el proceso de Enumeración. - Class Requests: Son los relativos a cualquier clase particular de dispositivo USB. Cada clase de dispositivo (excepto las clases genéricas o definidas por los fabricantes, vendor specific) está definida en una especificación de clase USB. Cada clase tiene sus requisitos propios, tales como tipo y número mínimo de Endpoints; además de otro tipo de Descriptor, que es llamado Report Descriptor (Descriptor de Reporte), el cual indica al Host cómo deben interpretarse los datos que envía el Esclavo al Host según la función del Esclavo. 2.3.3. Descriptores
Los Descriptores son tablas en las que los Esclavos almacenan información sobre sus características. Dichas tablas son no modificables por el Host (grabadas en ROM). Los
Ingeniero en Automática y Electrónica Industria
73
Fernando Bueno Zambruno
Proyecto Final de Carrera
Descriptores son jerárquicos. Algunos pueden contener información relativa a String Descriptors, que tienen información para que el Host muestre al usuario. El Host solicita los Descriptores a los esclavos USB mediante las Control Transfers. Los Descriptores que están en los niveles más altos de la jerarquía de Descriptores, informan al Host sobre la existencia de los Descriptores que están en los niveles más bajos de la jerarquía de Descriptores. A continuación se describen los Descriptores comunes a todos los tipos de Esclavos USB: - Device Descriptor: Contiene información sobre el máximo tamaño de paquete que soporta el Endpoint0, cuántas configuraciones soporta el Esclavo, y otra información. Es el primero que lee el Host. - Configuration Descriptor: Existe uno por cada posible forma de operar del Esclavo (solo puede haber una configuración activa en un determinado momento). Tiene información sobre cuántas Interfaces existen por configuración. - Interface Descriptor: Tiene información sobre el número de Endpoints (excepto el Endpoint0) que utiliza al Interface y sobre la Clase a la que pertenece. - Endpoint Descriptor: Existe uno por cada Endpoint de una Interface. Tiene información sobre el número de Endpoint. También sobre el tipo de Transferencia que realiza (Control, Interrupt, Bulk o Isochronous). Y también tiene información sobre el tamaño máximo de paquete del Endpoint. A continuación se describen los Descriptores básicos de las Clases USB: - Class Descriptor: Especifica a qué Clase USB pertenece una Interface. También da información sobre la longitud del Report Descriptor. - Report Descriptor: Tiene información sobre cómo debe el Host interpretar las Transferencias del Esclavo. Su estructura es totalmente diferente al del resto de los Descriptores. En el contexto del USB, una Transferencia es equivalente a un Report, y es un bloque de datos que el Host puede interpretar. 2.4. Modelo de capas del USB
El USB tiene su modelo particular de capas. En el cual existen 3 de ellas. Por lo general, las 2 capas superiores se implementan en software y la inferior en hardware, tanto en el Esclavo como en el Host. La Function Layer (Capa de Función) está formada por las Interfaces. Cada Interface es está formada por un grupo de Endpoints. Los datos que se mueven no tienen formato USB, tienen la estructura definida en el Report Descriptor; son los datos que corresponden al par Client Software-Function.
Ingeniero en Automática y Electrónica Industria
74
Fernando Bueno Zambruno
Proyecto Final de Carrera
En la USB Device Layer (Capa de Dispositivo USB), la información transmitida entre los pares son las Transacciones; que son entre Endpoints y Buffers.(estos últimos, implementados en software en el Host). En la USB Bus Interface Layer, los datos intercambiados entre los pares son Transacciones. Estas Transacciones están encuadradas dentro de los Frames generados a intervalos regulares de tiempo por el Host Controller.
2.5. Clase HID
El nombre HID es la abreviatura de “Human Interface Devices”. Esta Clase, cuya versión actual es el estándar HID 1.11 fue ideada con el propósito de englobar a dispositivos que permitan la interacción del usuario (ser humano) con el Host. Por lo tanto, los requerimientos de ancho de banda son mínimos, y la transferencia de datos debe ser confiable. Los datos que los dispositivos HID envían al Host son interpretados por el HID Class Driver del sistema operativo, para luego poder ser utilizados por la aplicación que los requiera (Client Software). Los dispositivos HID se comunican con el HID Class Driver mediante la (Default) Control Pipe o mediante Interrupt Pipes. Los requisitos para la implementación de un dispositivo HID son: - Control Endpoint (Endpoint0): obligatorio - Interrupt IN Endpoint: obligatorio - Interrupt OUT Endpoint: opcional 2.6. Hardware utilizado Ingeniero en Automática y Electrónica Industria
75
Fernando Bueno Zambruno
Proyecto Final de Carrera
Para la implementación del Esclavo USB se utilizó un MCU Freescale (ex Motorola) 68HC908JB8 de 8 bits. El MCU utilizado posee un módulo de Interfaz de bus USB (Esclavo) cuyos registros de datos y control están mapeados en la memoria del MCU. El módulo USB embebido en el MCU posee 3 Endpoints. El máximo payload de los Endpoints de este dispositivo es de 8 bytes. El Endpoint0 es de Control. El Endpoint1 es de tipo Interrupt IN. El Endpoint2 puede configurarse para funcionar como Interrupt IN o como Interrupt OUT. Todos los Endpoints del MCU utilizado son low-speed, por lo que tienen una tasa de transferencia de 1,5 Mbits/s; por lo que la máxima tasa de transferencia de datos útiles es de 64 Kbits/s. El módulo USB del MCU se configuró para trabajar de modo que produzca una Interrupción cada vez que se produzca un evento de envío o recepción en el bus USB. 2.7. Firmware desarrollado
El firmware del MCU en que se implementó el Esclavo USB HID se escribió en lengua je C. El módulo USB del MCU se encarga de generar y decodificar los paquetes adicionales a los datos, los CRCs, el bit stuffing y la codificación NRZI. Por lo que el firmware debe encargarse de interactuar con los Drivers del sistema operativo. Para que el Esclavo USB funcione correctamente, el firmware del MCU debe cumplir con los siguientes: - Contener los Descriptores. - Implementar los Estados posibles de un Esclavo USB (Finite State Machine). - Procesar los Standard Requests. - Procesar los Class Requests. - Enviar los Reports de acuerdo a lo definido en su(s) Report(s) Descriptor(s). 2.7.1. Implementación de Descriptores
Cada tipo de Descriptor se declaró como una estructura “const struct”, y luego se inicializaron con sus correspondientes valores, excepto el Report Descriptor, que se declaró como un arreglo de caracteres. Ya que cuando el Host solicita el Configuration Descriptor, el Esclavo debe enviar este y todos sus Descriptores subordinados en forma concatenada, se creó una estructura que los contuviera a todos; y es esta estructura la que se devuelve cuando se solicita el Configuration Descriptor. 2.7.2. Implementación de Standard Requests Ingeniero en Automática y Electrónica Industria
76
Fernando Bueno Zambruno
Proyecto Final de Carrera
Cada Standard Request se implementó como una función separada en el firmware. Para identificar el tipo de Standard Request enviado por el Host, se leen los campos de bits del paquete SETUP de la Control Transfer que contiene al Request. La determinación de los parámetros del Standard Request solicitado también se hace en base a una variable que forma parte del SETUP Packet. Como ejemplo, puede citarse que al Standard Request GetDescriptor() le corresponde un parámetro que es el Descriptor solicitado. Los parámetros son dependientes del Request. Existe un flag en los registros de control del módulo USB del Esclavo, que indica cuando un paquete es de SETUP. 2.7.3. Implementación de Class Requests
La decodificación de los Class Requests se hace con el mismo método que la decodificación de los Standard Requests. Con la única salvedad que los Class Requests y sus parámetros son con referencia a la Clase USB implementada. 2.7.4. Reports
Todos los Reports (enviados al Host cuando este los solicita mediante el Endpoint correspondiente) deben tener siempre el tamaño y estructura declarados en el Report Descriptor, los cuales dependen de la función a implementar en el Esclavo; en caso de no cumplirse esto, el Host los recibirá, pero los descartará. 2.7.5. Otras consideraciones del firmware
El firmware debe, además, ser capaz de procesar un Bus Reset, el cual es una señal enviada por el Host, y que es señalada por un flag en un registro de estado del MCU. 3. Resultados
Los resultados del trabajo se obtuvieron trabajando sobre una PC Host cuyo sistema operativo era Microsoft Windows XP, la funcionalidad que presta el firmware implementado funciona de igual forma independientemente del Host al que se conecte el dispositivo, ya que el USB es un Estándar de comunicación independiente de la plataforma y la Clase HID es también un Estándar. Por lo tanto, el dispositivo implementado debe funcionar de igual forma en cualquier Host que corra un sistema operativo que tenga un HID Class Driver. Como ejemplos de uso de la Clase HID, se implementó el firmware para un teclado USB, y también el firmware para un Mouse USB. No se implementó la parte física del Ingeniero en Automática y Electrónica Industria
77
Fernando Bueno Zambruno
Proyecto Final de Carrera
teclado ni del mouse porque no eran los objetivos del trabajo. A pesar de esto, se simuló el funcionamiento del teclado y el del mouse, ambos en forma satisfactoria.
4. Discusión
Debido a que la programación MCUs involucra registros de hardware (programación de bajo nivel), el único código que es migrable sin modificaciones a otro MCU es el código que corresponde a los Descriptores. Lo anterior se debe a que cada fabricante de MCUs define los registros de datos y control de la Interfaz de bus USB a su manera. Por lo anterior, es importante tener un conocimiento de cómo funciona el USB. Sobre todo en caso de tener que migrar a un MCU de otro fabricante. En caso de necesidad de implementar un Esclavo USB que sea de otra Clase USB, habrá que verificar la disponibilidad del Class Driver en el sistema operativo del Host. Además, habrá que verificar los tipos, la cantidad, y la velocidad de los Endpoints que se necesitan para implementar dicha Clase; y, en base a estos requerimientos, seleccionar el MCU a utilizar para la implementación de la Clase. Pueden realizarse aplicaciones que hagan uso del bus USB, pero que no estén englobadas dentro de ninguna Clase USB. Existen varias formas de hacer esto, y que no presentan mayor dificultad a la hora de escribir el firmware para el esclavo. Pero, en estos casos, deberá escribirse un Driver dedicado que haga uso de las funciones del núcleo (kernel) del sistema operativo para el control del bus USB. Es importante hacer notar que los Reports deben siempre ser del tamaño y estructura declarados en el Report Descriptor; de otra manera, el Class Driver del sistema operativo del Host no reconocerá y descartará los Reports, aunque el Esclavo los envíe. 5. Conclusión
Se logró implementar un firmware que cumpla con las pautas de comunicación básicas establecidas para un dispositivo USB de Clase HID. El firmware se escribió en lengua je C. Como aplicaciones de la Clase HID se simuló un teclado y también un mouse. Pero, también podrían haberse simulado dispositivos tales como keypads, joysticks, indicadores, etc. Hasta puede realizarse adquisición de datos de baja velocidad (hasta 64 Kbits/s) utilizando el hardware con Interrupt Endpoints low-speed que se utilizó y sirviéndose del HID Class Driver del sistema operativo. Utilizando MCUs que dispongan de Inte-
Ingeniero en Automática y Electrónica Industria
78