fernando bueno zambruno proyecto final de...
TRANSCRIPT
ESCUELA SUPERIOR DE INGENIEROS DE SEVILLA
INGENIERÍA EN AUTOMÁTICA Y ELECTRÓNICA INDUSTRIAL
PROYECTO FINAL DE CARRERA DISEÑO E IMPLEMENTACIÓN DE UNA
TARJETA DE CONTROL CON COMUNICACIÓN USB
Tutor: José Luís Mora Jiménez
Autor: Fernando Bueno Zambruno
Sevilla, septiembre de 2013
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 2
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 3
INDICE DE CONTENIDOS
Introducción …………………………………… 5
Objeto del proyecto …………………………………… 5
Diseño de la tarjeta …………………………………… 7
Diseño del esquemático …………………………………… 10
Etapa de alimentación …………………………………… 10
Etapa del microcontrolador …………………………………… 11
Diseño de la PCB …………………………………… 14
Programación del microcontrolador …………………………………… 15
Aplicación del PC …………………………………… 16
Diseño de la aplicación …………………………………… 16
Funcionamiento del sistema …………………………………… 18
Funciones de comunicación …………………………………… 18
Diseño de los controles …………………………………… 23
Control Calefactor y de Humidificador …………………………………… 23
Control temperatura y control humedad …………………………………… 23
Presupuesto …………………………………… 27
Recursos Humanos …………………………………… 27
Coste de los componentes y fabricación del PCB …………………………………… 27
Coste Total …………………………………… 28
Conclusión …………………………………… 30
Bibliografía …………………………………… 31
ANEXO 1:Programas del microcontrolador …………………………………… 32
APLICACION_HID.c …………………………………… 32
APLICACION_HID.h …………………………………… 35
shtxx.h …………………………………… 36
Descriptor_easyHID.h …………………………………… 41
ANEXO 2:Programas de la aplicación del PC …………………………………… 47
Form1.cs …………………………………… 47
EasyHID.cs …………………………………… 61
ANEXO 3: Bibliotecas de CCS …………………………………… 63
ANEXO 4: USB- Firmware para dispositivo esclavo
USB de Clase HID
…………………………………… 66
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 4
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 5
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 :
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 6
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 7
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 .
Figura 1.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 8
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.
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 9
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 10
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 11
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 12
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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 13
resistiva y por lo tanto no acumularía
energía.
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 14
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 15
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 16
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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 17
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 18
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).
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 19
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 varia-
ble 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 conecta-
do 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 20
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 me-
moria 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, BUF-FER_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)
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 21
{ 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 prin-
cipal del programa.
Para saber si el dispositivo estaba conectado/desconectado o enviando datos, nece-
sitábamos leer los diferentes mensajes que entregaba el controlador. Por lo que nece-
sitamos 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 buf-fer de entrada. break; } } base.WndProc(ref message);
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 22
}
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 ac-
ciones 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 23
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 encon-
trarí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 defini-
do 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 re-
les. 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 24
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. Kp es la
ganancia proporcional, Ti es la constante de tiempo integral y Td es la constante de
tiempo derivativa.
En el dominio s, el controlador PID se puede escribir como:
El controlador PID tiene tres parámetros (Kp, Ti, Td ) 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 con-
trol PID basado en un análisis del sistema a lazo abierto, ya que no contienen integra-
dores ni polos complejos. Siendo su respuesta a una entrada escalon similar a la de la
mostrada en la figura.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 25
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:
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 26
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 me-
nor al tiempo de establecimiento del sistema en lazo abierto. En el modelo de Zie-
gler/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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 27
PRESUPUESTO
Para realizar una estimación del coste económico del proyecto, se ha dividido los gas-
tos 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 circui-
to.
No se han contabilizado recursos hardware necesarios para la realización del proyec-
to, tales como soldador, multímetro, ordenador, ya que se trata de un proyecto uni-
versitario 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 apli-
cará 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, implemen-
tación montaje y teste o de la placa de prueba del PCB. Resumiendo y concretando
horas:
Tarea Horas Coste
Análisis de requisitos 25 227,25
Diseño 70 636,3
Implementación 135 1227,15
Experimentación 50 454,5
TOTAL 280 2545,2€
Coste de los componentes y fabricación del PCB
Podemos realizar un presupuesto del proyecto detallando los componentes utilizados:
Cantidad Elemento Precio IMPORTE
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 28
Unitario
1 SHT75 32,25 32,25
1 Transformador 12,3 12,3
1 PIC 18F2550 5,01 5,01
1 Puente diodo 0,7 0,7
1 Cristal 20MHz 0,27 0,27
1 Conector Hembra USB pla-
ca
0,6 0,6
1 Pulsador 0,41 0,41
1 LED Rojo/Verde 0,18 0,18
1 Zócalo 0,75 0,75
3 Transistor BD437 0,18 0,54
1 Triac BT136 0,68 0,68
1 MOC3041M 1,05 1,05
3 Diodo 1N4446 0,08 0,24
7 Bornas 0,45 3,15
1 Regulador 7805 0,57 0,57
1 Regulador 7812 0,65 0,65
2 Condensador 22pF 0,09 0,18
2 Condensador 100nF 0,12 0,24
1 Condensador electrolítico
47uF
0,25 0,25
2 Condensador electrolítico
470uF
0,4 0,8
2 Condensador electrolítico
4700uF
1,05 2,1
1 Resistencia 330 0,2 0,2
1 Resistencia 360 0,2 0,2
1 Resistencia 180 0,2 0,2
3 Resistencia 2K2 0,04 0,12
2 Resistencia 10K 0,08 0,16
1 Resistencia 1K 0,08 0,08
1 Placa virgen 11,8 11,8
-------- TOTAL ------ 75,68€
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 29
Coste Total
Reuniendo los costea anteriores y añadiendo el IVA correspondiente del 21%
CONCEPTO COSTE
Componentes y Fabricación
Circuito
75,68€
Recursos Humanos 2545,2€
TOTAL 2620,88€
IVA 21% 550,38€
TOTAL 3171,26€
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 30
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 su-
puesto 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 contro-
les, 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 impli-
carí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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 31
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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 32
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 <stdlib.h> #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 <pic18_usb.h> // Drivers's USB del PIC18F2550. #include <APLICACION_HID.h> // Definición de funciones y hardware utilizado en el programa. #include <Descriptor_easyHID.h> // Descriptores HID del proyecto. #include <USB.c> // 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;
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 33
void main(void) // Función Princi-pal. { // Variables globales. int8 recibe[USB_EP1_RX_SIZE]; // Declaramos la variable recibe de 32 bytes. int8 envia[USB_EP1_TX_SIZE]; // Declaramos la variable envía de 32 bytes. int8 valorPWM1=0; // Variable que contiene el valor del PWM1. int8 valorPWM2=0; // Variable que contiene el valor del PWM2. char ctemp[6], chumed[6]; shtxx_init(); set_tris_b(0b11111100); // RB0, RB1 salidas, el resto entradas. output_b(0x01); // Inicializamos las salidas a 0, excepto RB0. set_tris_c(0b00111000); 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(); // Inicializamos el stack USB. usb_task(); // Habilita el periferico usb y las interrupciones. USB_debug(); // Nos muestra el estado de conección del USB. while (TRUE) // Bucle infinito. { if(usb_enumerated()) // 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); // Enviamos el pa-quete de datos por USB.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 34
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. } } } }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 35
APLICACION_HID.h
// Definición de constantes y funciones: #define LED_GREEN PIN_C6 // Led USB_OK...( Dispositivo conectado al host ). #define LED_RED PIN_C7 // Led USB_ERROR... ( Dispositivo no detectado ). #define PWM1 PIN_C2 // Control PWM1. #define PWM2 PIN_C1 // Control PWM2. // Constantes varias: #define USB_CONFIG_HID_TX_SIZE 32 // Definimos el tamaño del endpoint de salida. #define USB_CONFIG_HID_RX_SIZE 32 // Definimos el tamaño del endpoint de entrada. // Comandos de entrada. #define ACTIVA_SALIDAS 0x0A // Comando para activar LED'S. #define PWM_CONTROL1 0x0B // Comando para activar PWM1. #define PWM_CONTROL2 0x0C // Comando para activar PWM2. #define LED_1 0x10 // Cambia de estado el LED 1. #define LED_2 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();
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 36
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); //data high output_low(shtxx_clk); //clk low delay_us(1); output_high(shtxx_clk); //clk high delay_us(1); output_low(shtxx_data); //data low delay_us(1); output_low(shtxx_clk); //clk low delay_us(2); output_high(shtxx_clk); //clk high delay_us(1); output_float(shtxx_data); //data high delay_us(1); output_low(shtxx_clk); //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); //clk low if((iobyte & mask) > 0) output_float(shtxx_data); //data high if MSB high else output_low(shtxx_data); //data low if MSB low delay_us(1);
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 37
output_high(shtxx_clk); //clk high delay_us(1); mask = mask >> 1; //shift to next bit } //Shift in ack output_low(shtxx_clk); //clk low delay_us(1); ack = input(shtxx_data); //get ack bit output_high(shtxx_clk); //clk high delay_us(1); output_low(shtxx_clk); //clk low return(ack); } //***** 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); //data low delay_us(1); output_high(shtxx_clk); //clk high delay_us(2); output_low(shtxx_clk); //clk low delay_us(1); output_float(shtxx_data); //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);
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 38
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;
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 39
shtxx_transstart(); //alert SHTxx ack = shtxx_write_byte(0x03); //send measure temp command and read ack status if(ack == 1) return; shtxx_wait(); //wait for SHTxx measurement to complete iobyte = shtxx_read_byte(); //read SHTxx temp data return(iobyte); } //***** Function to measure SHTxx RH ***** int16 shtxx_measurehumid(void) { int1 ack; int16 iobyte; shtxx_transstart(); //alert SHTxx ack = shtxx_write_byte(0x05); //send measure RH command and read ack status if(ack == 1) return; shtxx_wait(); //wait for SHTxx measurement to complete iobyte = shtxx_read_byte(); //read SHTxx temp data return(iobyte); } //***** 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)
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 40
{ shtxx_reset(); //reset SHTxx delay_ms(20); //delay for power-up }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 41
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 0x07D0 // PID easyHID, configurado por el usuario. #endif #ifndef USB_CONFIG_VID #define USB_CONFIG_VID 0x1781 // VID easyHID, configurado por el usuario. #endif #ifndef USB_CONFIG_BUS_POWER // El rango válido es 0...500. #define USB_CONFIG_BUS_POWER 100 // 100mA, corriente máxima que entregará el puerto. #endif #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 32 // Configurado por el usuario según aplicación. #endif #ifndef USB_CONFIG_HID_RX_SIZE // El rango válido es 0-255 #define USB_CONFIG_HID_RX_SIZE 32 // Configurado por el usuario según aplicación. #endif #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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 42
#ifdef USB_EP1_TX_ENABLE #undef USB_EP1_TX_ENABLE #endif #define USB_EP1_TX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for IN bulk/interrupt trans-fers #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 USB_ENABLE_INTERRUPT //turn on EP1 for OUT bulk/interrupt transfers #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 <usb.h> ////////////////////////////////////////////////////////////////// /// /// HID Report. Tells HID driver how to handle and deal with /// received data. HID Reports can be extremely complex, /// see HID specifcation for help on writing your own. /// ////////////////////////////////////////////////////////////////// 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, // Logical minimum (-128) 0x25, 0x7F, // Logical maximum (127) 0x75, 8, // Report size = 8 (bits) 0x95, USB_CONFIG_HID_TX_SIZE, // Report count 32 bytes 0x81, 2, // Input (Data, Var, Abs) 0x19, 1, // Usage minimum 0x29, 8, // Usage maximum 0x75, 8, // Report size = 8 (bits) 0x95, USB_CONFIG_HID_RX_SIZE, // Report count 32 bytes 0x91, 2, // Output (Data, Var, Abs)
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 43
0xc0 // 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 41 //config+interface+class+endpoint+endpoint (2 end-points) 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 configura-tions) ==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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 44
#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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 45
//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) /// //////////////////////////////////////////////////////////////////
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 46
//the offset of the starting location of each string. offset[0] is the start of string 0, offset[1] is the start of string 1, etc. char USB_STRING_DESC_OFFSET[]={0,4,12}; // 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 47
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.");
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 48
} 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 && Easy-HID.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 && Easy-HID.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 = (char)BufferINP[1]; temp2 = (char)BufferINP[2]; temp3 = (char)BufferINP[3]; temp4 = (char)BufferINP[4]; temp5 = (char)BufferINP[5]; temp6 = (char)BufferINP[6]; ctemp = temp2.ToString() + temp3.ToString() + ',' + temp5.ToString() + temp6.ToString(); tempcamp = Convert.ToDouble(ctemp); ctemp += " ºC";
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 49
label11.Text = ctemp; humed1 = (char)BufferINP[7]; humed2 = (char)BufferINP[8]; humed3 = (char)BufferINP[9]; humed4 = (char)BufferINP[10]; humed5 = (char)BufferINP[11]; humed6 = (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 buf-fer 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 50
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;
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 51
} } 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 du-ty_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) {
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 52
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)
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 53
{ //Si 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)) + (In-te_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 > 246) { ayuda_t = 246; Suma_Er_t = 0; } if (ayuda_t < 0) { ayuda_t = 0; Suma_Er_t = 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 disposi-tivo: 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) {
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 54
Er_h = SP_h - humedcamp; //Error ayuda_h = Sal_ant_h + ( Prop_h * Er_h) + (Derv_h * (Er_h - Er_ant_h)) + (In-te_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 disposi-tivo: 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 con-trol 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;
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 55
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 con-trol 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 56
} 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; }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 57
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) + '%';
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 58
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 du-ty_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 >= Convert.ToInt16(textBox2.Text)) { ciclo_calef = Convert.ToInt16(textBox2.Text); label8.Text = string.Format("{0:D}", ciclo_calef) + " sg"; textBox2.Text = ""; } } 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 = ""; } }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 59
} 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 disposi-tivo: // 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; }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 60
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 dis-positivo: // 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; } } }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 61
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 pDa-ta); [DllImport("mcHID.dll")] private static extern bool WriteEx(UInt32 pVendorId, UInt32 pProductId, IntPtr pDa-ta); [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")]
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 62
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; } } }
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 63
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 infi-
nito 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 inicializa-
ció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 inte-
rrupció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), devuel-
ve TRUE si que pin es alto. Persona siempre devuelve TRUE.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 64
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 buf-
fer está todavía llena con el último paquete.
usb_puts (Punto final, los datos, len, tiempo de espera) Envía los siguientes da-
tos 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 da-
tos 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 men-
saje puede contener más de un paquete. Devuelve el número de los bytes reci-
bidos.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 65
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 contro-
ladores PIC con un periférico USB interno.
usbn960x.h Controladores de hardware para la capa externa Nacional
USBN9603/USBN9604 periféricos USB. Usted puede usar este periférico ex-
terno USB para añadir a cualquier 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 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 66
ANEXO 4: USB
Firmware para dispositivo esclavo USB de Clase HID Emanuel G. Aguirre, Pablo A. Di Giulio
Universidad Tecnológica Nacional, Facultad Regional San Francisco
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 pe-
rifé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 de-
rivados 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 de tipo serial), en la eliminación de la amplia variedad de conec-
tores (siendo todos reemplazados por un solo tipo).
La alternativa actual al USB es el bus FireWire (IEEE-1394), creado por Apple Com-
puter. 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 es que es más útil para periféricos de 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, 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
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 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 67
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 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 68HC908JB8 (Hard-
ware).
-Freescale CodeWarrior Development Studio v5.7 para MCUs 68HC(S)908 (Compila-
dor).
-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 utiliza-
ron para analizar las estructuras de datos del sistema operativo que reflejan el tráfi-
co en el bus USB y así depurar el Firmware del MCU.
2.1. Arquitectura del bus USB
Para lograr un mínimo grado de comprensión del presente trabajo, es necesario anali-
zar los elementos del bus USB desde el punto de vista de los sistemas
de 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 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).
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 68
- Interconexión USB (USB interconnect).
Existe sólo un Host en un sistema USB. Los dispositivos USB proveen servicios o fun-
ciones 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 69
Dentro de cada dispositivo lógico USB existen uno o más Endpoints, que son recepto-
res 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 po-
lled. 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 pro-
pias 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 de-
nominadas D+ y D-. Además se proveen 2 líneas de alimentación y de masa respectiva-
mente, 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-Return-
To-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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 70
En el USB, lo datos se envían en paquetes. A su vez, los Paquetes se agrupan para for-
mar 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 re-
cibidos 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 perte-
nece 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 71
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 direc-
tamente 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 transfe-
rencia 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 Transferen-
cia. 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 End-
point 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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 72
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.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 73
- 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 man-
tiene 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 crea-
das 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 (Transfe-
rencias 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 comien-
zan 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. Ca-
da 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 Escla-
vo 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 74
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 informa-
ción sobre el número de Endpoint.
También sobre el tipo de Transferencia que realiza (Control, Interrupt, Bulk o Isoch-
ronous). Y también tiene información sobre el tamaño máximo de paquete del End-
point.
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 gene-
ral, las 2 capas superiores se implementan en software y la inferior en hardware, tan-
to 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 forma-
to USB, tienen la estructura definida en el Report Descriptor; son los datos que co-
rresponden al par Client Software-Function.
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 75
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, im-
plementados en software en el Host).
En la USB Bus Interface Layer, los datos intercambiados entre los pares son Tran-
sacciones. 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 ver-
sión actual es el estándar HID 1.11 fue ideada con el propósito de englobar a dispositi-
vos 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) Con-
trol 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 76
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 End-
points 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 Inte-
rrupció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 ini-
cializaron con sus correspondientes valores, excepto el Report Descriptor, que se de-
claró 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 77
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 ci-
tarse 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 con-
trol 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 decodifi-
cació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 co-
rrespondiente) 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 imple-
mentado 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 plata-
forma y la Clase HID es también un Estándar. Por lo tanto, el dispositivo implementa-
do 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
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 78
teclado ni del mouse porque no eran los objetivos del trabajo. A pesar de esto, se si-
muló 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, selec-
cionar 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 engloba-
das 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 ope-
rativo 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-
Fernando Bueno Zambruno Proyecto Final de Carrera
Ingeniero en Automática y Electrónica Industria 79
rrupt Endpoints fullspeed, la tasa máxima podría llegar a 1,216 Mbits/s, sirviéndose
del HID Class Driver del sistema operativo del Host.