y seguiiimos con USB...
vamos a estudiar en forma la transmisión USB usando la biblioteca de vínculo dinámico
mpusbapi.dll y que mejor que empezar con el ejemplo de J1M. hay que tener una idea de porque Jaime utilizó esas llamadas a los drivers y que hace cada una de ellas.
hay muchas preguntas en cuanto a este tipo de transmisión, por ejemplo:
¿podemos transmitir a 12Mbits/seg en lenguaje C?
¿si estamos ejecutando un proceso de muestreo, podremos llegar a esa velocidad?
las posibles respuestas a estas, sin ánimo de desilusionar a nadie (incluyendome) vean esto (mitos y realidades página 9 de 858_USB.pdf):
una cosa que hay que tener en consideración y sobre todo para lo que estudiamos esto en lenguaje de alto nivel, el protocolo de transmisión USB también lo llaman la pila USB
tomado de usb_20.pdf, figura 5-2, pág26
observen que tiene cierto parecido al
modelo OSI. en las capas superiores tenemos las funciones basicas que el usuario puede realizar(comunicación lógica). esto a su vez va a parar a la segunda capa y luego a la tercera capa(comunicación física) que involucra el aspecto eléctrico. En nuestro caso estariamos directamente metidos en la capa superior, pero algunas veces entrando en las otras dos:
primera capa(superior): programación básica en C.
segunda capa(intermedio): llamados a los drivers que trae el compilador de C.
tercera capa(inferior): llamados a los drivers que trae el compilador de C (procesos dentro de los drivers) y conexión del módulo USB al HOST.
esta tema es fascinante pero a la vez extenso así que hay que ir por partes, para no perdernos dentro de esos
"jardines" como lo llama RedPic
- ESTUDIO DEL LADO DEL PIC:
por lo pronto lo que hay que hacer, es estudiar los drivers que trae el CCS (que es el compilador de C que uso), estos son:
- pic18_usb.h
- PicUSB.h
- usb.c
- usb.h
tratando de meter un poco de teoría a cada línea que nos encontremos y siguiendo el mismo método anterior: analizando el código a la inversa.
veamos el código reza así:
/////////////////////////////////////////////////////////////////////////
//// PicUSB.c ////
//// ////
//// Este ejemplo muestra como desarrollar un sencillo dispositivo ////
//// USB con el PIC18F2550, aunque puede ser facilmente adaptado ////
//// para la serie 18Fxx5x. Se suministra el PicUSB.exe, así como ////
//// su código fuente para Visual C# 2005, podréis encontrar tb ////
//// los drivers para el dispositivo. No se suministra esquema de ////
//// conexión puesto que está pensado para ser usado en el GTP USB, ////
//// cualquiera de las tres versiones disponibles, si aun no teneis ////
//// el programador, podeis utilizar el esquema de ese proyecto. ////
//// ////
//// Cuando el dispositivo sea conectado al PC, saldrá el asistente ////
//// para la instalación del driver. Instala el suministrado junto ////
//// a este ejemplo, lo encontrareis dentro de la carpeta Driver. ////
//// Una vez instalado podreis usar el PicUSB.exe para encender o ////
//// apagar el led bicolor del GTP USB, y para realizar la suma de ////
//// dos números introducidos. ////
//// ////
//// Realizado con el compilador CCS PCWH 3.227 ////
//// ////
//// Por: Jaime Fernández-Caro Belmonte hobbypic@hotmail.com ////
//// ////
//// http://www.hobbypic.com ////
/////////////////////////////////////////////////////////////////////////
#include <18F4550.h>
//#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL3,CPUDIV1,VREGEN
#fuses XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,NOPBADEN // el fuse
// modificado para USB -> VREGEN
#use delay(clock=48000000)
/////////////////////////////////////////////////////////////////////////////
//
// CCS Library dynamic defines. For dynamic configuration of the CCS Library
// for your application several defines need to be made. See the comments
// at usb.h for more information
//
/////////////////////////////////////////////////////////////////////////////
#define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
#define USB_EP1_RX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
#define USB_EP1_TX_SIZE 1 //size to allocate for the tx endpoint 1 buffer
#define USB_EP1_RX_SIZE 3 //size to allocate for the rx endpoint 1 buffer
/////////////////////////////////////////////////////////////////////////////
//
// If you are using a USB connection sense pin, define it here. If you are
// not using connection sense, comment out this line. Without connection
// sense you will not know if the device gets disconnected.
// (connection sense should look like this:
// 100k
// VBUS-----+----/\/\/\/\/\----- (I/O PIN ON PIC)
// |
// +----/\/\/\/\/\-----GND
// 100k
// (where VBUS is pin1 of the USB connector)
//
/////////////////////////////////////////////////////////////////////////////
//#define USB_CON_SENSE_PIN PIN_B2 //CCS 18F4550 development kit has optional conection sense pin
/////////////////////////////////////////////////////////////////////////////
//
// Include the CCS USB Libraries. See the comments at the top of these
// files for more information
//
/////////////////////////////////////////////////////////////////////////////
#include <pic18_usb.h> //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver
#include <PicUSB.h> //Configuración del USB y los descriptores para este dispositivo
#include <usb.c> //handles usb setup tokens and get descriptor reports
/////////////////////////////////////////////////////////////////////////////
//
// Al conectar el PicUSB al PC encendemos el Led Rojo hasta que el dispositivo
// halla sido configurado por el PC, en ese momento encederemos el Led Verde.
// Esperaremos hasta que se reciba un paquete proveniente del PC. Comprobaremos
// el primer byte del paquete recibido para comprobar si queremos entrar en el
// modo Suma, donde se realizará una suma de dos operandos, que corresponderan
// con los dos bytes restantes del paquete recibido; una vez realizada la suma
// enviaremos el paquete con el resultado de vuelta al PC. Si entramos en el
// modo Led comprobaremos el segundo byte del paquete recibido para comprobar
// si deberemos apagar los leds, encender el verder o el rojo.
//
/////////////////////////////////////////////////////////////////////////////
#define LEDV PIN_B6
#define LEDR PIN_B7
#define LED_ON output_high
#define LED_OFF output_low
#define modo recibe[0]
#define param1 recibe[1]
#define param2 recibe[2]
#define resultado envia[0]
void main(void) {
int8 recibe[3]; //declaramos variables
int8 envia[1];
LED_OFF(LEDV); //encendemos led rojo
LED_ON(LEDR);
usb_init(); //inicializamos el USB
usb_task(); //habilita periferico usb e interrupciones
usb_wait_for_enumeration(); //esperamos hasta que el PicUSB sea configurado por el host
LED_OFF(LEDR);
LED_ON(LEDV); //encendemos led verde
while (TRUE)
{
if(usb_enumerated()) //si el PicUSB está configurado
{
if (usb_kbhit(1)) //si el endpoint de salida contiene datos del host
{
usb_get_packet(1, recibe, 3); //cojemos el paquete de tamaño 3bytes del EP1 y almacenamos en recibe
if (modo == 0) // Modo_Suma
{
resultado = param1 + param2; //hacemos la suma
usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); //enviamos el paquete de tamaño 1byte del EP1 al PC
}
if (modo == 1) // Modo_Led
{
if (param1 == 0) {LED_OFF(LEDV); LED_OFF(LEDR);} //apagamos los leds
if (param1 == 1) {LED_ON(LEDV); LED_OFF(LEDR);} //encendemos led verde
if (param1 == 2) {LED_OFF(LEDV); LED_ON(LEDR);} //encendemos led rojo
}
}
}
}
}
allí estan las funciones usb_task(), usb_init() que ya las vimos por encimita, la novedad son las funciones: usb_wait_for_enumeration(), usb_get_packet y usb_put_packet
también estan las definiciones USB_EP1_TX_ENABLE USB_ENABLE_BULK ...
comencemos por el principio (lógico ¿no?
)
#define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID
¿que significa HID?
HID es acrónimo en español de Dispositivo de Interfaz Humana como bien lo describe Diego en su artículo
EL USB DESENCADENADO : HID USB
y resulta que en los driver´s del CCS viene activado por defecto.
...
//// USB_HID_DEVICE (TRUE) - HID devices require extra code to handle ////
//// HID requests. You can disable to save ////
//// ROM space if you are not using a HID ////
//// device. If you are not using a HID ////
//// device you must provide your own O/S ////
//// (Windows) driver. ////
...
...
//should the compiler add the extra HID handler code? Defaults to yes.
#IFNDEF USB_HID_DEVICE
#DEFINE USB_HID_DEVICE TRUE
#ENDIF
...
si lo vamos a usar, no colocamos nada. si NO lo usaremos, lo negamos en el define
(está programado al revés, digo yo
)
-------------------------------------------------------------------------
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
¿que es eso de endpoint? ¿bulk/interrup transfers?
voy a colocar aquí una explicación que me pareció excelente tomada del proyecto especial de grado
link cápitulo 3: Bus Serie Universal, funcionamiento pag 38
cita:
...
"Los dispositivos (o mejor dicho, las funcionas) tienen asociados unos canales
lógicos unidireccionales (llamados pipes) que conectan al host controlador con
una entidad lógica en el dispositivo 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á
reservado para el control del bus."
...
Se puede decir que los endpoint son buffer temporales de datos de entrada/salida en cada tipo. En el módulo USB existen 16 de salida (OUT) y 16 de entrada (IN) pero agrupados en forma bidirecional, de acuerdo a un par de bits de configuración (ver pag171). Ojo un endpoint es unidireccional.
para quien tenga dudas con el pipe, una imagen representativa:
ver pag33 usb_20.pdf
respecto a la otra pregunta, existen 4 tipos de transferencias:
· transferencias de control:
usado para comandos (y respuestas) cortos y simples. Es el tipo de transferencia usada por el pipe 0
· transferencias isócronas:
proveen un ancho de banda asegurado pero con posibles pérdidas de datos. Usado típicamente para audio y video en tiempo real
· transferencias interruptivas:
para dispositivos que necesitan una respuesta rápida (poca latencia), por ejemplo, mouse y otros dispositivos de interacción humana.
· transferencias masivas (BULK):
para transferencias grandes y esporádicas utilizando todo el ancho de banda disponible, pero sin garantías de velocidad o latencia. Por ejemplo, transferencias de archivos.
entonces nosotros debemos definir que tipo de transferencia vamos a realizar.
en el ejemplo de Jaime se estan declarando
4 endpoints (2 bidirecionales) como transferencia masiva o BULK.
corrección: son 2 endpoints (1 bidireccional)hay otra definición:
#define USB_EP1_TX_SIZE 1 //size to allocate for the tx endpoint 1 buffer
podemos definir el tamaño del buffer en bytes, el límite lo tendremos que buscar en el driver correspondiente (con FS hasta 64 bytes por transacción).
nota: no confundir FS con HS, con High Speed se llega hasta 480Mbps, el módulo USB del pic soporta Full Speed = 12Mbps
-----------------------------------------------------------------------------
usb_wait_for_enumeration() según el concepto que dan en el driver, hace lo mismo que usb_enumerated(). La diferencia está en que el primero se queda en un bucle indefinido hasta que el HOST le dé la orden de habilitación.
para propositos donde queramos hacer otras actividades en el PIC, usaremos el segundo.
-----------------------------------------------------------------
ahora hablaremos de como se hace para transmitir y recibir datos.
para eso estan usb_put_packet(,,) y usb_get_packet(,,)
ambas necesitan de 3 argumentos. vamos con put_packet:
...
/* usb_put_packet(endpoint,*ptr,len,toggle)
/*
/* Input: endpoint - endpoint to send packet to
/* ptr - points to data to send
/* len - amount of data to send
/* toggle - whether to send data with a DATA0 pid, a DATA1 pid, or toggle from the last DATAx pid.
/*
/* Output: TRUE if data was sent correctly, FALSE if it was not. The only reason it will
/* return FALSE is if because the TX buffer is still full from the last time you
/* tried to send a packet.
/*
/* Summary: Sends one packet out the EP to the host. Notice that there is a difference
/* between a packet and a message. If you wanted to send a 512 byte message you
/* would accomplish this by sending 8 64-byte packets, followed by a 0 length packet.
/* If the last (or only packet) being sent is less than the max packet size defined
/* in your descriptor then you do not need to send a 0 length packet to identify
/* an end of message.
...
- el 1 argumento endpoint ya se explicó.
- el 2 argumento apunta a la dirección del dato a enviar (es una variable declarada por nosotros).
- el 3 argumento es el tamaño del paquete en bytes.
- el 4 argumento habla sobre DATA1, DATA2, toggle. ¿y eso que es?
como parte de su protocolo, nos encontraremos entre otras cosas que USB maneja la transmisión de datos por paquetes, llamados TOKEN en la cuál el HOST es el iniciador de todas las transferencias que se producen en el BUS
[2] ver pag206 usb_20.pdf
pués bien en la parte de transmisión de datos USB, los paquetes de datos se encuentran en grupos de paquetes de datos, y dentro de estos, existen unos llamados DATA1, DATA2. hay un proceso llamado sincronización del data toggle. a grandes rasgos esto no es mas que un método de validación de paquetes, y lo que hace es enviar alternadamente a DATA1 y DATA2 en una secuencia seguido de su ACK respectivo. todo con el objetivo de mantener la sincronización transmisor <-> receptor.
CORRECCIÓN: ES DATA0 y DATA1 ver pag232 usb_20.pdf
ese tercer argumento definido en el código ejemplo: USB_DTS_TOGGLE
enum USB_DTS_BIT {USB_DTS_DATA1=1, USB_DTS_TOGGLE=2, USB_DTS_DATA0=0,
USB_DTS_STALL=3, USB_DTS_USERX=4};
según la página 174 de 39632c.pdf el data toggle está definido por un bit llamado DTSEN y es mas, allí lo explican.
hay toda una teoría en lo relacionado a los grupos de paquetes, no lo mencionaré para no salirme del objetivo principal.
no ha terminado, sigue-->