Una vez que ya tenemos el archivo cargado, vamos bajando y vemos la primer sentencia de código:
A través de la misma introducimos en nuestro programa la definición de registros internos del PIC18F2550, que es el microcontrolador usado en el entrenador USB. Con esto le decimos al compilador que trabajaremos con un PIC18 y que use el modo PCH (compilador para micros de 16 bits).
Más abajo tenemos la siguiente línea de código:
A través del uso de esta directiva, le decimos al compilador que trabajaremos con el
CAD (Convertidor analógico a digital) con 10 bits de resolución y justificación a la derecha (Los bits más significativos del registro ADRESH, son leídos como “0”).
Los “fuses” (o mejor dicho palabra de configuración) son muy importantes ya que determinan muchos parámetros del microcontrolador. Los que se encuentran ordenados en la figura anterior tienen las siguientes funciones:
- NOMCLR: No usaremos el pin MCLR, utilizaremos reset por software. - HSPLL: Utilizaremos un cristal de alta velocidad en conjunto con el PLL. - NOWDT: No utilizaremos el perro guardián.
- NOPROTECT: No protegeremos la memoria contra lecturas. - NOLVP: No habilitaremos la programación con bajo voltaje. - NODEBUG: No utilizamos código para debug.
- USBDIV: Significa que el reloj del módulo USB se tomará de la salida del PLL\2. En este caso 96Mhz \ 2 = 48Mhz (Frecuencia de trabajo).
- PLL5: Esto quiere decir que el prescaler dividirá en 5 la frecuencia del oscilador principal para obtener los 4Mhz necesarios en la entrada del PLL. Como estamos usando un cristal de 20Mhz….20Mhz \ 5 = 4Mhz.
- CPUDIV1: La frecuencia del PLL se dividirá en 2 para obtener la frecuencia de trabajo del CPU: 48Mhz.
- VREGEN: Habilitamos el regulador interno de 3.3v del módulo USB. - NOPBADEN: Todo el puerto B como entradas/salidas digitales.
Ahora ya tenemos la palabra de configuración explicada, por lo que procederemos con la siguiente sentencia:
Acá declaramos la frecuencia de trabajo para que el compilador pueda calcular los retardos en las funciones de delay.
Usando la directiva #include agregamos todas las librerías necesarias para gestionar la comunicación USB.
Por el momento explicaremos 2 de ellas: - APLICACIÓN_HID.h
- Descriptor_easyHID.h
En la primera guardamos todas las definiciones de hardware, constantes, declaración y documentación de funciones, etc.
A través del uso de etiquetas #define le añadimos nombres a las entradas y salidas para poder facilitar la programación y recordar donde se ubican los diferentes componentes.
Definimos el tamaño que debe tener el edpoint de entrada y de salida, en este caso el valor “32” nos indica que recibirá 32 bytes desde el host y transmitirá 32 bytes hacia el host.
Para dejar bien en claro el significado de endpoint, les dejo el siguiente enunciado: "Los dispositivos (o mejor dicho, las funciones) tienen asociados unos canales
lógicos unidireccionales (llamados pipes) que conectan al host (PC) controlador con una entidad lógica en el dispositivo (PIC) llamada endpoint. Los datos son enviados
en paquetes de largo variable (potencia de 2). Típicamente estos paquetes son de 64, 128 o más bytes.
Estos endpoints (y sus respectivos pipes) son numerados del 0 al 15 en cada dirección, por lo cual un dispositivo puede tener hasta 32 endpoints (16 de entrada y 16 de salida). La dirección se considera siempre desde el punto de
vista del host controlador. Así un endpoint de salida será un canal que transmite datos desde el host controlador al dispositivo. Un endpoint solo puede tener una única dirección. El endpoint 0 (en ambas direcciones) está
La cita anterior fue extraída del trabajo de grado que se encuentra en el siguiente link: http://pablohoffman.com/oscusb/doc/
Estas definiciones está íntimamente ligadas con las funciones de control que se
encuentran en la aplicación de Visual C# estudiada anteriormente. Esto lo veremos más adelante.
Estos macros son usados para el control de las salidas digitales para no tener que usar las funciones predefinidas más costosas de recordar. Ayudan al desarrollo del programa. Luego hay 2 funciones más, pero su explicación es redundante ya que están muy bien documentadas en la librería.
Siguiendo con el programa principal tenemos las siguientes funciones:
Estas 2 directivas nos permiten utilizar los puertos sin necesidad de configurarlos cada vez que se usan. Lo que si debemos tener en cuenta es configurar que pines serán entradas y que pines serán salidas usando las siguientes sentencias:
Una vez configurados los puertos pueden usarse libremente los que nos ahorra RAM y ROM dentro una vez que compilamos el programa.
Esta función ya se encuentra explicada en la librería, pero dentro de ella tiene una sentencia que no ha sido desarrollada.
Esta función se encuentra dentro de la librería: USB.c, lo que hace es esperar hasta que el host enumere el dispositivo y prender el LED verde. En caso de que no lo hiciera el dispositivo no puede comunicarse con la aplicación de control y el LED rojo permanece encendido.
Esta función pertenece al usuario
Palitroquez
del foro TODOPIC. La misma configura correctamente el ADCON2 como se explica en la librería:APLICACIÓN_HID.h
Adentrándonos en la función principal main (), tenemos las siguientes funciones:
A través de estas sentencias configuramos correctamente el CAD.
Con estas 2 sentencias inicializamos los 2 PWM y apagamos los led’s asociados.
Una función muy importante ya que inicializa el stack USB, conecta el dispositivo al host, habilita las interrupciones, etc. Trabaja en conjunto con USB stack; esta función asume que el USB está conectado siempre al host, por lo que mantiene al dispositivo permanentemente conectado.
Esta función inicializa el hardware USB, las interrupciones, etc. Trabaja en conjunto con
USB_init (). En caso de usar USB_init_cs (), hay que llamarla periódicamente desde el
main (), pero normalmente se llama una sola vez.
Esta función la utilizamos como indicador si el dispositivo está o no conectado al host. En caso de que no esté enumerado el programa no avanza y no nos podemos comunicar con la PC.
Ya dentro del bucle infinito que se muestra a continuación, todas las funciones que se encuentren dentro del mismo se ejecutarán indefinidamente.
Aquí miramos el estado de la función usb_enumerated (), la misma nos indica si el dispositivo está o no conectado. En caso de que si lo esté ejecuta las sentencias que se explicarán a continuación:
Estas 2 funciones son idénticas y nos permiten controlar el ciclo de trabajo de ambos PWM. La variable ValorPWMx_H, corresponde al semiciclo positivo de la señal y la variable ValorPWMx_L, al semiciclo negativo.
Modificando el valor de ambas variables según el valor que se reciba de la PC, podemos modificar el ciclo de trabajo, por lo tanto la potencia aplicada a los led’s PWM
Estas 4 sentencias son idénticas, lo que hacen es ver el estado de los pulsadores y enviar guardar el valor en el buffer “envía”.
Ambos grupos de sentencias son iguales, lo que hacen es lo siguiente, enumerado para una mejor comprensión:
- Elegimos un canal de conversión. - Esperamos 10us.
- Guardamos la parte alta de la conversión en la variable valor_adc_alta. - Guardamos la parte baja de la conversión en la variable valor_adc_baja. - Asignamos la parte alta al 4º bit del arreglo “envía”.
- Asignamos la parte baja al 5º bit del arreglo “envía”.
Esta función envía el arreglo de datos “envía” hacia el host para luego poder visualizar los datos en la aplicación. La función tiene 4 parámetros que enumeraremos para un mejor entendimiento:
1º parámetro: endpoint usado, en este caso el endpoint 1. Para su mejor entendimiento remitirnos a la explicación que está en la pág. 44.
2º parámetro: variable que contiene los datos a enviar, en este caso es el buffer “envía”. 3º parámetro: Tamaño del arreglo que contiene los datos a enviar: 32.
Al principio se ejecuta un bucle condicional, el mismo se fija, mediante la función que tiene asociada si hay datos en el endpoint indicado. En este caso dentro del paréntesis hay indicado un “1”, refiriéndose al endpoint 1.
En caso de que haya datos provenientes del host para leer, los tomamos y guardamos usando la función usb_get_packet. La misma tiene 3 parámetros:
1º parámetro: endpoint usado. Usamos el endpoint 1.
2º parámetro: variable donde se guardarán los datos provenientes del host. 3º parámetro: tamaño del paquete a recibir.
Una vez tomados los datos del host los procesaremos como muestran las siguientes funciones:
Acá tenemos 2 bucles condicionales, primero si recibimos el comando: ACTIVA _ SALIDAS, vemos el primer byte del arreglo “recibe”. Según sea el dato recibido, activamos el led en consecuencia.
Para darnos una idea de como trabaja la aplicación con el hardware vamos a poner la función:
Vemos que la función envía el comando para activar los led’s y luego el dato
correspondiente al led 1. Cuando el PIC los recibe, cambia el estado del led 1 como muestra la sentencia de código:
Para el caso de los PWM se cumple lo mismo: Se recibe el comando de control, recibe el valor procedente de las barras de desplazamiento y lo guarda en las variables que
contienen el ciclo de trabajo.
Con esto ya hemos visto tanto la aplicación para la PC, como el firmware para el
microcontrolador y estamos listos para poder crear nuestras propias aplicaciones a partir de la guía.
Algo que habíamos omitido hasta el momento es la explicación del hardware utilizado. Aunque su utilización sea sencilla vale la pena detallar como está compuesto, además es necesario nombrar los elementos que lo componen para un mejor entendimiento del mismo.