Autor Tema: USB en CCS: Detectando y gestionando la interrupción  (Leído 30605 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
USB en CCS: Detectando y gestionando la interrupción
« en: 01 de Abril de 2008, 19:03:29 »
Hola amigos.
Después de varios días peleando con el usb he sacado algunas cosillas en claro.Entre ellas cómo tener mi propia rutina de interrupción cuando se reciben datos por algún endpoint que no sea el endpoint 0,además de saber como acceder a los datos de forma más directa y controlada sin hacer uso de las funciones usb_gets, usb_get_packet o el consabido usb_kbhit.
No se si esto resulta válido para todas las clases de dispositivo,pero bueno,ahí va.

Las rutinas implicadas son usb_isr (pic18_usb.h), usb_isr_tok_out_dne (usb.c) y usb_isr_tok_dne() (pic18_usb.h)

Lo que yo he hecho es traerme a mi fichero principal las rutinas usb_isr y usb_isr_tok_dne() desde el fichero pic18_usb.h y colocar al final de usb_isr_tok_dne() una llamada a la que es mi rutina de interrupción (datos_endpoint)

Código: CSS
  1. #int_usb
  2. void usb_isr() {
  3.  
  4.    if (usb_state==USB_STATE_DETACHED) return;   //should never happen, though
  5.    if (UIR) {
  6.       debug_usb(debug_putc,"\r\n\n[%X] ",UIR);
  7.       if (UIR_ACTV && UIE_ACTV) { usb_isr_activity();}  //activity detected.  (only enable after sleep)
  8.       if (UCON_SUSPND) return;
  9.       if (UIR_UERR && UIE_UERR) {usb_isr_uerr();}          //error has been detected
  10.       if (UIR_URST && UIE_URST) {usb_isr_rst();}        //usb reset has been detected
  11.       if (UIR_IDLE && UIE_IDLE) {usb_isr_uidle();}        //idle time, we can go to sleep
  12.       if (UIR_SOF && UIE_SOF) {usb_isr_sof();}
  13.       if (UIR_STALL && UIE_STALL) {usb_isr_stall();}        //a stall handshake was sent
  14.       if (UIR_TRN && UIE_TRN) {
  15.          usb_isr_tok_dne();
  16.          UIR_TRN=0;    // clear the token done interrupt., 0x190.3
  17.       }  
  18.    }
  19. }
  20.  
  21.  
  22. void usb_isr_tok_dne() {
  23.    int8 en;
  24.    en=USTAT>>3;
  25.          debug_usb(debug_putc,"T ");
  26.          debug_usb(debug_putc,"%X ", USTAT);
  27.       if (USTAT==USTAT_OUT_SETUP_E0) {   //new out or setup token in the buffer
  28.          debug_usb(debug_putc,"%X ", EP_BDxST_O(0));
  29.          if ((EP_BDxST_O(0) & 0x3C)==USB_PIC_PID_SETUP) {
  30.             EP_BDxST_I(0)=0;   // return the in buffer to us (dequeue any pending requests)
  31.             debug_usb(debug_putc,"(%U) ", EP_BDxCNT_O(0));
  32.             debug_display_ram(EP_BDxCNT_O(0), usb_ep0_rx_buffer);
  33.             usb_isr_tok_setup_dne();
  34.             //if setup_0_tx_size==0xFF - stall ep0 (unhandled request)
  35.             //if setup_0_tx_size==0xFE - get EP0OUT ready for a data packet, leave EP0IN alone
  36.             //else setup_0_tx_size=size of response, get EP0OUT ready for a setup packet, mark EPOIN ready for transmit
  37.             if (__setup_0_tx_size==0xFF)
  38.                usb_flush_out(0,USB_DTS_STALL);
  39.             else {
  40.                usb_flush_out(0,USB_DTS_TOGGLE);
  41.                if (__setup_0_tx_size!=0xFE) {
  42.                   usb_flush_in(0,__setup_0_tx_size,USB_DTS_USERX);
  43.                }
  44.             }
  45.             UCON_PKTDIS=0;       // UCON,PKT_DIS ; Assuming there is nothing to dequeue, clear the packet disable bit
  46.          }
  47.          else if ((EP_BDxST_O(0) & 0x3C)==USB_PIC_PID_OUT) {
  48.             usb_isr_tok_out_dne(0);
  49.             usb_flush_out(0,USB_DTS_TOGGLE);
  50.             if ((__setup_0_tx_size!=0xFE)&&(__setup_0_tx_size!=0xFF)) {
  51.                usb_flush_in(0,__setup_0_tx_size,USB_DTS_DATA1);   //send response (usually a 0len)
  52.             }
  53.          }
  54.       }
  55.       else if (USTAT==USTAT_IN_E0) {   //pic -> host transfer completed
  56.          __setup_0_tx_size=0xFF;
  57.          usb_isr_tok_in_dne(0);
  58.          if (__setup_0_tx_size!=0xFF)
  59.             usb_flush_in(0,__setup_0_tx_size,USB_DTS_TOGGLE);
  60.          else
  61.             usb_init_ep0_setup();
  62.       }
  63.       else {
  64.          if (bit_test(USTAT,2)) {
  65.             usb_isr_tok_in_dne(en);
  66.          }
  67.          else {
  68.             usb_isr_tok_out_dne(en);
  69.             datos_endpoint(en);
  70.          }
  71.       }
  72. }

Dicha rutina se ejecuta cuando se reciben datos del host por el endpoint "en".Este endpoint siempre será distinto del endpoint 0,por lo que las transferencias de control del pic con el host nos serán transparentes y no nos molestarán.

Ahora bien ¿y dónde están los datos recibidos?
Cuando el módulo usb está habilitado,el pic coloca a partir de la posición de memoria 0x400 lo que viene a llamarse "Buffer Descriptor Table",que contiene una serie de registros que permiten gestionar los datos que fluyen a través de los endpoints,ya sea para transferencias OUT (PC -> PIC) como para transferencias IN (PIC -> PC)



Esta tabla de descriptores está ubicada al inicio del banco 4 y contendrá tantos descriptores (Buffer Desciptors) como endpoints use nuestro pic (por partida doble,UN DESCRIPTOR PARA IN ENDPOINT y OTRO PARA OUT ENDPOINT).
O sea,si en nuestro pic sólo usamos el Endpoint 1 (para IN y para OUT) en la tabla de descriptores de buffer tendríamos 4 descriptores,los dos del endpoint 0 (esos siempre deben estar,según las especificaciones) y los dos del endpoint 1.

En la imagen se ve el descriptor de buffer correspondiente al OUT Endpoint 0 (0x400-0x403)...consecutivo a él estaría el correspondiente al IN Endpoint 0 (0x404-0x407)...después estaría el OUT Endpoint 1 (0x408-0x40B)....etc

Hay que recordar que IN y OUT se consideran SIEMPRE desde la perspectiva del host,por lo que una transferencia OUT será siempre en el sentido PC -> PIC....así que los datos que se reciben desde el pc los gestionaremos siempre con el correspondiente EPn OUT Buffer Descriptor (siendo "n" el endpoint en cuestión).

Cada descriptor de buffer está compuesto de 4 registros:

BDnSTAT: Registro de estado del descriptor de buffer.
BDnCNT: Número de bytes (recibidos o a transmitir,según sea un IN Endpoint o un OUT Endpoint).
BDnADRL: Byte menos significativo de la dirección de comienzo de los datos.
BDnADRH: Byte más significativo de la dirección de comienzo de los datos.

Los bits 0 y 1 del registro BDnSTAT (CB8 y CB9) se usan para concatenarlos a los 8 del registro BDnCNT en el caso de transferencias de más de 256 bytes.
El bit 7 (UOWN) del mismo registro indica quien tiene la posesión del descriptor y su correspondiente búffer de datos.

¿y qué quiere decir esto?
Pues sencillo.

Tanto el CORE del micro (el programador y su programa,a fin de cuentas) como la SIE (módulo interno que gestiona el bus usb) tienen acceso a los descriptores y sus búferes,y tanto uno como el otro pueden realizar operaciones de escritura sobre ellos,por lo que el micro implementa una especie de semáforo (bit UOWN) para indicar cuando la SIE tiene el control y no se deben realizar operaciones de escitura ni en el descriptor ni en el búfer.

Los bits restantes del registro de estado y sus respectivas funciones cambian en función de quien tiene el control de los bufers.Os animo a que echeis un ojo al datasheet para saber un poco más de esto (Capítulo 17.4.1.2).

El registro BDnCNT no tiene mayor misterio...indica la cantidad de bytes recibidos tras una transferencia en un OUT Endpoint y deberá ser escrito con el valor adecuado en el caso de una transferencia a través de un IN Endpoint.

Los registros BDnADRH y BDnADRL son lo que son,un puntero para saber donde tenemos que ir a buscar los datos para transferencias OUT o un puntero para que la SIE sepa dónde tiene que ir a buscar los datos en una transferencia IN.

Hay que tener presente una cosa...el pic da la posibilidad de usar los buffers en modo Ping-Pong.Esto permite que,en vez de tener un descriptor y un buffer por cada endpoint (cuando digo "cada" estoy considerando IN Enpoint y OUT Endpoint por separado),tengamos todo por partida doble (descriptores y buffers).Dicha cosa permitiría por ejemplo que mientras la SIE está enviando un paquete de datos al host nosotros estemos escribiendo otro paquete al mismo tiempo.Esto para la agilización de las tasas de transferencia viene de perlas  :) aunque,por lo visto,el modo Ping-Pong no es fácil de domar.
En función de cual de los 4 modos Ping-Pong se use,la estructura de los descriptores cambia,por lo que hay que conocer el valor de los bits PPB1:PPB0 (registro SFR UCFG ) para determinar donde están nuestros descriptores.



Y bueno,un ejemplo de lo que pudiera ser la rutina de interrupción sería (considerando el modo Ping-Pong desactivado):

Código: CSS
  1. #define BD1OSTAT        0x408
  2. #define BD1OCNT         0x409
  3. #define BD1OADRL        0x40A
  4. #define BD1OADRH        0x40B
  5.  
  6. int8 buffer_usb_in[64];
  7.  
  8. void datos_endpoint(int8 ep)
  9. {
  10.   int8 i,bytes_recibidos,*ptr;
  11.   bytes_recibidos = *(BD1OCNT + ep*8);
  12.   ptr = 256*(*(BD1OADRH + 8*ep)) + (*(BD1OADRL + ep*8));
  13.   for(i = 0; i < bytes_recibidos; i++)
  14.     {
  15.     buffer_usb_in[i] = *ptr;
  16.     ptr++;
  17.     }
  18.   usb_flush_out(ep, USB_DTS_TOGGLE);  
  19. }

No hay que olvidar la llamada a usb_flush_out,que básicamente lo que hace es habilitar el endpoint para seguir recibiendo datos.

Pos lo dicho,espero que sea provechoso.
Saludo.






« Última modificación: 11 de Octubre de 2008, 17:31:14 por Modulay »

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #1 en: 02 de Abril de 2008, 01:34:31 »
Jose, no se si aplaudirte desde aquí o salir corriendo a Málaga a besarte a tornillo.  :D :D :D

Me voy a estudiar tu post como si fuese para un final de curso. Muchas gracias por abrir la brecha en este muro.  :mrgreen:
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Azicuetano

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1020
    • Aplicaciones Electrónicas en Alicante.
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #2 en: 02 de Abril de 2008, 03:14:13 »
Mmmhhh... información de la buena...  :D 

Thanks.


Un saludo desde Alicante.

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #3 en: 02 de Abril de 2008, 09:00:32 »
Me alegra que os sea de utilidad :)

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #4 en: 02 de Abril de 2008, 16:36:29 »
hey, muchas gracias por la información Modulay

una pregunta, y el consumo de recursos, ¿bajó? o ¿se mantiene en el 56%?

pd: lo extraño es que compilando tanto en el 2550 como en el 4550, siempre sale el 56%  :shock:

La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #5 en: 02 de Abril de 2008, 17:32:27 »
Imagino que te refieres al consumo de ram.
Ambos micros cuentan con la misma cantidad de ram por lo que es normal que el consumo sea el mismo para los dos.

Yo estoy usando en estos momentos el 18F66J50 y estoy compilando al 86% de la capacidad de la ram.Aunque,resulta muy curioso el hecho de que la barra aparezca de color amarillo cuando normalmente lo hace de color verde.



Y me da a mí que es debido al hecho de que la habilitación del módulo usb implica que los bancos 4,5,6 y 7 (25% aproximado del total de ram del 18F66J50,50% del total de ram de los 18Fxx5x) quedan a disposición de la SIE para los descriptores de búffer y los búfferes de datos,por lo que el compilador la considera como ram consumida,pero aún así,disponible para el usuario.



Estos 4 bancos también pueden ser usados para almacenar tus datos,pero hay que tener cuidado ya que en un momento dado la SIE los puede machacar con lo que recibe del bus,y por supuesto,no se te ocurra toquetear el banco 4 a no ser que sepas que estás usando zonas de memoria que no contienen descriptores de búffer.A esto se suma el hecho de que,según el datasheet,no hay mecanismo hardware que impida la escritura o la lectura sobre una zona concreta de ram que esté en ese momento poseída por la SIE (el bit UOWN correspondiente lo indicará),pero advierte que pueden ocurrir cosas raras cuando se haga

Desconozco que criterio sigue la SIE a la hora de colocar los datos procedentes del host,pero supongo que obedecerá a un patrón concreto y acorde a los descriptores de búffer existentes.

Para saciar la curiosidad,lo que se puede hacer es declarar varios arrays a los bestia ubicados en ese espacio de memoria (0x400-0x7FF) y ver si al compilar aumenta el consumo de ram...apostaría a que no
« Última modificación: 02 de Abril de 2008, 17:34:37 por Modulay »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #6 en: 02 de Abril de 2008, 18:25:23 »
Esto es un lujo de información, gracias Modulay por compartirla con todos nosotros  :mrgreen:

Saludos.

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #7 en: 02 de Abril de 2008, 18:49:31 »
Lo que me temía...ni se ha despeinado,oye  :D


Desconectado J1M

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1960
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #8 en: 02 de Abril de 2008, 19:23:36 »
Gracias por la explicación, la verdad siempre viene bien eso de 'olvidar' un poco lo que nos dan hecho, puesto que muchas veces podemos arrancarle unos cuantos bytes de optimización :)

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #9 en: 03 de Abril de 2008, 06:19:37 »
Yo también te lo agradezco Modulay. Cuando pueda intentaré comprender esta valiosa información.

Un saludo

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #10 en: 03 de Abril de 2008, 06:43:54 »
Gracias por la explicación, la verdad siempre viene bien eso de 'olvidar' un poco lo que nos dan hecho, puesto que muchas veces podemos arrancarle unos cuantos bytes de optimización :)

No estaríamos hablando hoy de esto si en su día tú no nos hubieras abierto la puerta a este mundillo del usb.
Yo tan sólo puse una piedrecita más sobre lo que otros construyeron antes.
Y esperemos que siga creciendo!!

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #11 en: 03 de Abril de 2008, 16:04:53 »
uuy Modulay se me está haciendo agua la boca con esos candentes tips acerca del usb, me dan ganas de dejar de hacer el proyectico en que estoy metido para meterle al usb.. pero no puedo :(

hay una sospecha que tengo, referente al gran consumo de ram, puede que exista la posibilidad que esos codigos de ccs esten usando todos los endpoint y por ello se lleva tanta memoria. Habría que investigar (y es  otro reto que tengo) de hacer una modificación para usar un solo endpoint (con la interrupción por supuesto), y con ello reducir mas lineas.

en otros "driver" que he visto en la carpeta PICC, son demasiado genericos, y sacan muchas cuentas inutiles en proyectos particulares.


La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #12 en: 03 de Abril de 2008, 17:47:33 »
El tema de los endpoints va encapsulado en el descriptor de configuración del dispositivo (cosa que el compilador no va a saber interpretar) y, aparte de eso, no hay más que un par de DEFINES por ahí que se añaden al programa para habilitar los endpoints que vayas a usar,de cara a las rutinas.

Localiza esta línea en la parte de arriba del fichero pic18_usb.h...

 #define USB_TOTAL_BUFFER_SPACE  ((int16)0x300)

...cambia el 0x300 por 0x200, 0x100, etc...compila y observa lo que pasa...

Después,un poco más abajo,localiza esta y coméntala:

#reserve 0x400:0x4FF+USB_BUFFER_NEEDED

...vuelve a compilar....y observa.

Ahora vuelve a poner la primera que cambiaste con el valor que tenía originalmente,localiza esta línea (allá por la línea 414) y coméntala...compila...

No se trata de que el compilador active endpoints a nuestras espaldas...lo que si hace es reservar memoria para sus búfferes y para los descriptores de los búfferes.

Pero ahora que has sacado el tema,hay una cosa que no mo cuadra,y es la coexistencia de esto...

Código: [Seleccionar]
char usb_data_buffer[USB_TOTAL_BUFFER_SPACE-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_LENGTH

// Array que ocupa al completo (quitando los búferes de EP0 IN y EP0 OUT,o sea 16 bytes) los bancos 5, 6 y 7 = 752 bytes

...con esto

Código: [Seleccionar]

#reserve 0x400:0x4FF + USB_BUFFER_NEEDED

// Reserva el banco 4 para los descriptores de buffer,vale,eso me parece bien...pero,si ya tenemos declarado un array que ocupa prácticamente toda la USB RAM y que contendrá los búfferes de todos los endpoints exceptuando el EP0 ¿¿¿[size=15pt]POR QUÉ[/size] reserva también USB_BUFFER_NEEDED bytes??


Me parece algo redundante teniendo en cuenta que el valor de USB_BUFFER_NEEDED depende de los endpoints que usemos y del tamaño que les pongamos...a más endpoints y más tamaño,más gorda es la redundancia.

Código: [Seleccionar]

#define USB_BUFFER_NEEDED (USB_EP0_TX_SIZE+USB_EP0_RX_SIZE+USB_EP1_TX_SIZE+USB_EP1_RX_SIZE+USB_EP2_TX_SIZE+USB_EP2_RX_SIZE
+USB_EP3_TX_SIZE+USB_EP3_RX_SIZE+USB_EP4_TX_SIZE+USB_EP4_RX_SIZE+USB_EP5_TX_SIZE+USB_EP5_RX_SIZE
+USB_EP6_TX_SIZE+USB_EP6_RX_SIZE+USB_EP7_TX_SIZE+USB_EP7_RX_SIZE+USB_EP8_TX_SIZE+USB_EP8_RX_SIZE
+USB_EP9_TX_SIZE+USB_EP9_RX_SIZE+USB_EP10_TX_SIZE+USB_EP10_RX_SIZE+USB_EP11_TX_SIZE+
USB_EP11_RX_SIZE+USB_EP12_TX_SIZE+USB_EP12_RX_SIZE+USB_EP13_TX_SIZE+USB_EP13_RX_SIZE+
USB_EP14_TX_SIZE+USB_EP14_RX_SIZE+USB_EP15_TX_SIZE+USB_EP15_RX_SIZE)


Yo uso 2 endpoints de 64 bytes.Tengo estas dos líneas en mi programa:

#define USB_EP1_TX_SIZE    64
#define USB_EP1_RX_SIZE    64

Lo que quiere decir que CCS me está consumiendo,por redundancia en la compilación, 128 bytes más de los que debe.

 :?







« Última modificación: 03 de Abril de 2008, 17:53:54 por Modulay »

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #13 en: 03 de Abril de 2008, 17:52:51 »
Al final,de forma indirecta,has dado en el clavo.
Ese array no debería estar declarado de esa forma.Da por sentado de que los endpoints van a ocupar toda la USB RAM...y yo en mi caso solo ocupo 128 bytes,además de los 16 del EP0

Debería bastar con la directiva #reserve...esa si reserva la cantidad justa de RAM

Desconectado Modulay

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 2651
Re: USB en CCS: Detectando y gestionando la interrupción
« Respuesta #14 en: 03 de Abril de 2008, 18:14:20 »
Me he cepillado el array,he programado el micro...sigue funcionando perfectamente...

Pero he pasado de esto...





...a esto...