Bueno pues les traigo noticias sobre mis problemas
Al fin he logrado que el PIC18F2550 pueda enviar 50 bytes cada 1ms usando modo HID.
Se sabe que el HID trabaja hasta 64kB/s en Full Speed. Esto se debe a que se configura un polling de 1ms con paquetes de 64bytes, por lo que en 1s se tienen 64,000 bytes enviados. HID en USB 2.0 no permite el uso de más de 2 endpoints: uno del tipo control para el setup y otro para datos interruptivo. Si hubiera más EP's la historia sería otra.
Al usar el ejemplo HID que CCS trae estos tiempos son imposibles de alcanzar ya que las librerías vienen muy generalizadas y ejecutan muchas instrucciones que toman mucho tiempo.
En uno de tantos intentos integré el uso del modo ping pong en el pic, pero por más que le busqué no encontré porqué el pic se desenumeraba
Tuve que estudiar el funcionamiento de los Buffer Descriptors, la SIE y el procedimiento de envío de paquetes y para lograr un envío de 50kB/s esto es lo que hice.
- Configurar el HID con endpoint 1tx de 50 bytes de tamaño y 1ms de polling. El endpoint 1rx puede tener cualquier tamaño y polling (por ahora no lo usaré excesivamente).
- Mi muestreo se hace en una subrutina con el timer0 cada 40us, escribiendo 2 bytes por evento, teniendo 50bytes cada 1ms. Esta subrutina escribe en el primer buffer clon... directamente sobre la USB ram. Cuando se llena el buffer pasa al segundo buffer y así sucesivamente. En total hay 4 buffers de 50 bytes cada uno, localizados en estas direcciones de la usb ram (todas en el banco 6):
Buffer1 0x0600 a 0x0631
Buffer2 0x0632 a 0x0663
Buffer3 0x0664 a 0x0695
Buffer4 0x0696 a 0x06C7
- Cuando algún buffer se ha llenado entonces en el main se llama una función de envío USB que hace lo siguiente:
+ Recibe el número del buffer que está listo para envío
+ Espera indefinidamente que la SIE deje de controlar el endpoint 1
+ Escribe en el BD_CNT del EP1 el número 50 que equivale a los bytes a enviar por paquete (o reporte)
+ Modifica el ADRL_EP1 y ADRH_EP1 con la dirección ram del inicio del buffer a enviar
+ Invierte el bit DTS que sirve para dar paridad a los paquetes, de forma tal que si el paquete anterior es un DATA0, entonces el siguiente sea un DATA1
+ Por último levanta la bandera de la SIE que activa el envío del buffer cuando el HOST lo indique. Esto sucederá exactamente cada 1ms.
La ventaja de usar 4 buffers clones es que mientras la SIE está manipulando los 50 bytes de un buffer, yo me doy el lujo de estar preparando otro buffer para enviarlo. Esto es similar al modo ping pong, pero con trampa
Otra ventaja que tiene mi función de envío es que el buffer ya está ubicado en la usb ram. La función de CCS mueve un buffer de la ram del usuario a la usb ram y eso toma tiempo. También se tarda porque espera a que la SIE pierda control de la ram, mientras que mi función garantiza que la usb ram está más que lista para salir del pic.
void usb_put_fast_1(int8 buffer, int8 len)
{
//Espera a que la SIE deje de manipular el endpoint
while(bit_test(*(STATUS_EP1_IN),UOWN));
//Indica cuántos bytes se enviarán (no hace falta escribir BC9:BC8)
*(CNT_EP1_IN) = len;
//Indica el comienzo del buffer a la SIE
//El byte alto siempre es 0x06
*(ADRH_EP1_IN) = 0x06; //primer buffer
//El byte bajo sí cambia
switch(buffer)
{
case 1:
*(ADRL_EP1_IN) = 0x00; break;
case 2:
*(ADRL_EP1_IN) = 0x32; break;
case 3:
*(ADRL_EP1_IN) = 0x64; break;
case 4:
*(ADRL_EP1_IN) = 0x96; break;
}
//Invierte el bit de datos de paquete DTS (data toggle sync) y
//activa el envío del endpoint levantando UOWN
if(bit_test(*(STATUS_EP1_IN),DTS))
*(STATUS_EP1_IN)= 0b10001000; //UOWN=1, DTS=0, DTSEN=1
else
*(STATUS_EP1_IN)= 0b11001000; //UOWN=1, DTS=1, DTSEN=1
}
Después para verificar que el pic realmente estuviera enviando los datos en tiempo y forma usé un analizador usb por software y otro por hardware.
En la imagen se ve que cada que el HOST envía un paquete del tipo Start Of Frame también envía un paquete IN al pic pidiéndole los 50 bytes. El SOF en Full Speed es cada 1ms por lo que el PIC funciona como debe.
Y bueno... ahora la segunda problemática... leer desde Windows esos paquetes cada 1ms sin perderlos. Difícil porque Windows no garantiza ejecución de código en tiempos tan pequeños... pero bueno. A ver qué se me ocurre