Autor Tema: Detectando Flancos de subida (y de bajada) con un 18F4550  (Leído 8261 veces)

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

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Detectando Flancos de subida (y de bajada) con un 18F4550
« en: 10 de Septiembre de 2008, 17:49:53 »
Lo tengo "casi" todo, y ese "casi" hace que esté contento con el resto. Quiero tenerlo todo y vamos a ver si con vuestra ayuda lo logro porque me tiene desconcertado.  :mrgreen:

Introducción:

Los que me conocéis de antiguo estáis al tanto de un proyecto que acometí cuando mis conocimientos de los PIC's no eran los que son hoy, que no es que sean muchos pero si que son bastantes mas que en aquella época.

El proyecto era el Analizador lógico de 3 Canales y a pesar de todo me siento me siento muy orgulloso de él que sin embargo era difícil de manejar, sobre todo a la hora de iniciar el "sensado" y también después en el PC que había que "tratar" los datos recibidos para una correcta visualización.

Además de haber, digamos, "ampliado" el tema con cosas como la Serie Técnicas en C y los Decodificando el protocolo ABA Track 2 y el El Protocolo Wiegand explicado que tratan mas o menos con el mismo asunto.

Decidí hace poco re-acometer el tema pero empezando de nuevo desde el principio, de forma mas simple, con un paso intermedio de transmisión de datos en un formato en que no hiciese falta un Software especial, como mi Analyzer realizado en Delphi, sino que en modo texto pudiesen recogerse las medidas y que después pudiesen ser tratadas algebraica o gráficamente con cualquier programa "ad hoc", incluso con el mio en Delphi pero no solo con él.

Me he tirado a la piscina y he empezado con el asunto, leyendo flancos de subida (y de bajada) y transmitiendo los resultados vía serie para poder recogerlos en el PC y guardarlos en modo texto. Después veré que hago con los datos y con qué software.

Lo primero es lo primero y eso es lo que he atacado, recoger flancos, guardarlos y transmitirlos.

Procedimiento

Aunque mas abajo os pongo el fuente en CCS de lo que llevo hecho primero os describiré lo que he implementado.

Utilizo un 18F4550 rodando a 48 Mhz, usando la PLL para obtener la máxima velocidad posible. A esta velocidad el Timer1 de 16 bits tiene un ciclo de Tick de 0.0833 uS y un desborde (Overflow) de 5,461333 mS.

Los distintos desbordes del Timer1 los recojo sobre una variable con la #int_timer1.

Senso al inicio los PIN_B0, PIN_B1 y PIN_B2 para conocer su estado y con esta información configuro las #int_ext, #int_ext1 y #int_ext2 de forma que interrumpan a la llegada del flanco correspondiente al cambio al estado contrario. Así si leo un H en PIN_B0 configuro #int_ext para H_TO_L y si leo al inicio un L la configuro para recibir el flanco L_TO_H.

A partir de ahí habilito las tres interrupciones mas la del Timer1 y me quedo a la espera de la llegada de los distintos flancos, sean estos los que sean. Cuando llega el primero, por cualquiera de los tres canales pongo a cero tanto el Timer1 como el contador de Overflows del Timer1 para que los distintos flancos sean recogidos en unidades de tiempo de distancia del Timer1 con respecto a ese primer flanco.

Tengo en RAM tres ARRAYS donde voy a guardar los datos de cada flanco cuando llegue por cada una de las tres interrupciones. Estos datos van a ser:

  • Canal por el que llega (1,2 ó 3) y si es L_TO_H ó H_TO_L
  • Valor del Timer1 (16 bits) en el momento de su llegada
  • Número de Desbordes del Timer1 ocurridos en el momento de su llegada

Cada vez que llega un flanco Conmuto la interrupción correspondiente al flanco contrario y me tomo nota del siguiente flanco que estoy esperando.

Cuando lo estimo oportuno le pido al PIC que me envíe los datos y entonces vuelca la información recogida en los ARRAYS al canal serie y puedo o visualizarlos o recogerlos en el PC.

El formato es:

<D,Index,Channel,Overflows,Timer>

Con estos datos es fácil calcular el tiempo de llegada de cada flanco ya que es:

timeF = (Overflows * 5461,333 uS) + (Timer  0.0833 uS) expresado en microsegundos.

Aquí tenéis un ejemplo de una lectura realizada en la que se ven datos de los tres canales:



Paso ahora a mostraros el código fuente pertinente que he escrito:

Fuses y configuración:
Código: C#
  1. #fuses HSPLL,NOMCLR,PUT,BROWNOUT,BORV43,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,CCP2B3
  2. #use delay(clock=48000000)
  3. #use rs232(baud=115200, xmit=TTL_TX, rcv=TTL_RX)
  4.  
  5. #define MaxRecords 255
  6.  

Variables en RAM:
Código: C#
  1. int8  timer1_overflows;
  2. char  next_Interrupt_1;
  3. char  next_Interrupt_2;
  4. char  next_Interrupt_3;
  5. int8  flagFirstEdge;
  6.  
  7. char   ChannelEdge[MaxRecords];
  8. int8   Overflows[MaxRecords];
  9. int16  Timer[MaxRecords];
  10. int8   Index;
  11.  

Código de Inicialización del sistema:
Código: C#
  1. void limpia_data_records(void){
  2.  
  3.    for(Index=0;Index<MaxRecords;Index++){
  4.       ChannelEdge[Index] = 0x00;
  5.       Overflows[Index]   = 0x00;
  6.       Timer[Index]       = 0x00;
  7.    }
  8.    Index=0;
  9. }
  10.  
  11. void exec_COMMAND_START(void){
  12.  
  13.    limpia_data_records();
  14.    flagFirstEdge = 0x00;
  15.    ext_int_edge(0,H_TO_L); next_Interrupt_1='L';
  16.    ext_int_edge(1,H_TO_L); next_Interrupt_2='L';
  17.    ext_int_edge(2,H_TO_L); next_Interrupt_3='L';
  18.    if( !input(PIN_B0) ){ ext_int_edge(0,L_TO_H); next_Interrupt_1='H'; }
  19.    if( !input(PIN_B1) ){ ext_int_edge(1,L_TO_H); next_Interrupt_2='H'; }
  20.    if( !input(PIN_B2) ){ ext_int_edge(2,L_TO_H); next_Interrupt_3='H'; }
  21.    enable_interrupts(int_ext);
  22.    enable_interrupts(int_ext1);
  23.    enable_interrupts(int_ext2);
  24.    enable_interrupts(int_timer1);
  25.  
  26.    printf("<%c,%c,%c,%c>\r\n",COMMAND_ACK,next_Interrupt_1,next_Interrupt_2,next_Interrupt_3);
  27. }
  28.  

Y las interrupciones para la Captura de Datos:
Código: C#
  1. // Base de Tiempos //////////////////////////////////////////////////
  2. #int_timer1
  3. void timer1_handler(void){
  4.  
  5.    ++timer1_overflows;         // One Tick: 0.0833 uS, One Overflow: 5,461333 mS
  6.  
  7. }
  8.  
  9. // Canal 1 : RB0 ////////////////////////////////////////////////////
  10. #int_ext
  11. void channel_1_handler(void){
  12.  
  13.    if(flagFirstEdge==0x00){
  14.       flagFirstEdge=0x01;
  15.       timer1_overflows=0x00;
  16.       set_timer1(0);
  17.    }
  18.  
  19.    ChannelEdge[Index] = '1' + next_Interrupt_1;
  20.    Overflows[Index]   = timer1_overflows;
  21.    Timer[Index]       = get_timer1();
  22.    ++Index;
  23.  
  24.    if(next_Interrupt_1=='H'){
  25.       ext_int_edge(0,H_TO_L);
  26.       next_Interrupt_1='L';
  27.    }
  28.    else{
  29.      ext_int_edge(0,L_TO_H);
  30.      next_Interrupt_1='H';
  31.    }
  32.  
  33. }
  34.  
  35. // Canal 2 : RB1 ////////////////////////////////////////////////////
  36. #int_ext1
  37. void channel_2_handler(void){
  38.  
  39.    if(flagFirstEdge==0x00){
  40.       flagFirstEdge=0x01;
  41.       timer1_overflows=0x00;
  42.       set_timer1(0);
  43.    }
  44.  
  45.    ChannelEdge[Index] = '2' + next_Interrupt_2;
  46.    Overflows[Index]   = timer1_overflows;
  47.    Timer[Index]       = get_timer1();
  48.    ++Index;
  49.  
  50.    if(next_Interrupt_2=='H'){
  51.       ext_int_edge(1,H_TO_L);
  52.       next_Interrupt_2='L';
  53.    }
  54.    else{
  55.      ext_int_edge(1,L_TO_H);
  56.      next_Interrupt_2='H';
  57.    }
  58. }
  59.  
  60. // Canal 3 : RB2 ////////////////////////////////////////////////////
  61. #int_ext2
  62. void channel_3_handler(void){
  63.  
  64.    if(flagFirstEdge==0x00){
  65.       flagFirstEdge =0x01;
  66.       timer1_overflows=0x00;
  67.       set_timer1(0);
  68.    }
  69.  
  70.    ChannelEdge[Index] = '3' + next_Interrupt_3;
  71.    Overflows[Index]   = timer1_overflows;
  72.    Timer[Index]       = get_timer1();
  73.    ++Index;
  74.  
  75.    if(next_Interrupt_3=='H'){
  76.       ext_int_edge(2,H_TO_L);
  77.       next_Interrupt_3='L';
  78.    }
  79.    else{
  80.      ext_int_edge(2,L_TO_H);
  81.      next_Interrupt_3='H';
  82.    }
  83. }
  84.  

Como podéis ver el tratamiento en las tres interrupciones externas es absolutamente idéntico. Y en cualquiera de ellas se controla únicamente si es el primero en llegar y se inicializa Timer1 en lo que corresponde mediante el código:

Código: C#
  1.    if(flagFirstEdge==0x00){
  2.       flagFirstEdge=0x01;
  3.       timer1_overflows=0x00;
  4.       set_timer1(0);
  5.    }
  6.  


Conclusiones y pregunta

Aquí tenéis un fichero de texto con un volcado completo de datos leídos.

Las primeras líneas son:
<D,  0,3,L,125,    6>             
<D,  1,1,L,  0,  339>             
<D,  2,1,H,  0, 2396>             
<D,  3,1,L,  0, 4444>             
<D,  4,1,H,  0, 6500>             
...

"Teóricamente" debería andar todo bien, y tal como se ve en la datos recogidos (las pruebas las he hecho con un dispositivo emisor de los trenes de pulsos conocido y sé que los tiempos recibidos son compatibles al 100% con lo esperado). Tengo perfectamente el canal, el tipo de flanco y el tiempo de llegada de cada uno de los flancos .... menos el primero.  :5]

<D,  0,3,L,125,    6>             

El primero se recoge en el canal 3, #int_ext2 como debe ser, y por lo tanto el trocito de código que controla el flag flagFirstEdge se ejecuta en él, timer1_overflows y el timer1 se ponen a cero e inmediantamente se guardan donde se debe ... de echo el valor del timer1 de esa primer línea parece ser también correcto ya que 6 es lo esperado que tardaría en recogerse tras ejecutar el propio código de dentro de la interrupción.

Pero lo que no es posible creerse que los overflows sean 125. Simplemente no es posible.

Si se ejecuta timer1_overflows=0x00; y tres lineas mas abajo se ejecuta    Overflows[Index]   = timer1_overflows; no es posible que se recoja 125 en lugar de 0x00. Y Index no ha podido cambiar .... uffff

Lo dicho estoy desconcertado con este único "bug" y no se a que es debido.

Y quiero saberlo.

¿Se os ocurre algo?  :shock: :shock: :shock:
« Última modificación: 10 de Septiembre de 2008, 18:16:43 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17502
    • MicroPIC
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #1 en: 11 de Septiembre de 2008, 03:31:40 »
Don Diego, llevo un rato mirando y no encuentro el bug. Pienso que por alguna razón no está entrando en el bloque de código de inicialización determinado por flagFirstEdge=0 pero no veo cuál es esa razón.

No obstante, hay algo en tu programa que a mí particularmente no me gusta hacer, y no sé si tendría algo que ver. Has puesto un printf justo detrás de activar las interrupciones externas y del timer.
Como el printf es un proceso lento, que ejecuta mucho código y luego envía la información al puerto serie, quizás haga algo dentro que provoque ese efecto perverso que estás detectando con 125 overflowses.

En fin, seguro que tocando aquí y allá darás con la tecla y será una tontería, como suele ocurrir con estas cosas.

Por cierto, te digo otro truquito que yo he usado a veces y ahorra un montón de ciclos de reloj: el flanco (L o H) por el que salta una interrupción está en un bit. Si pones ese bit a 0 o 1 es lo mismo que si haces ext_int_edge(0,L_TO_H); o ext_int_edge(0,H_TO_L);

Esto es lo que hago en mi minidimmer. Aquí tendrás que poner la dirección de memoria del byte y la posición del bit en cuestión. Este código es válido para un 12F683:
#bit  SENTIDO_INTERRUPCION = 0x01.6

y en la interrupción haces esto:
SENTIDO_INTERRUPCION = ~SENTIDO_INTERRUPCION; // cambiamos de flanco la int.

Te aseguro que se ahorran un montón de ciclos de reloj. Compila y verás la diferencia.




Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #2 en: 11 de Septiembre de 2008, 08:25:02 »
 :shock: :shock: :shock: :shock: :shock: :shock:

Si ya me lo dijo mi agüela: Ten güenos amigos y verás que bien se vive.  :mrgreen:

1º.- Lo del printf no me s'había ocurrido pero puedes tener mas razón c'un santo.
2º.- Lo del bit de flanco no solo me gusta sino que leyéndolo puedo quitarme de enmedio la variable next_Interrupt_x donde me guardo exactamente lo mismo.

Genial, Manolo, genial. (sobre todo si es verdad) :D :D :D

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #3 en: 13 de Septiembre de 2008, 05:46:04 »
 :shock: :shock: :shock: :shock: :shock:

Mi desconcierto no tiene límites, es infinito, imponderable por su tamaño, es monstruoso, total, absoluto.

Implementadas todas la sugerencias de M.Nocturno el único cambio notado es que en ludar de obtener un primer dato de la forma <D,0,3,L,125, 6>   lo obtengo como <D,0,3,L,65,6> o sea que he pasado de tener un vicecontador de overflows de 125 a uno de 65 ...

Los bits de flanco están definidos por su ubicación física en el mapeado del PIC:

Código: C#
  1. #define INTCON2 0xFF1
  2.  
  3. #bit Edge1 = INTCON2.6
  4. #bit Edge2 = INTCON2.5
  5. #bit Edge3 = INTCON2.4
  6.  

El printf está ahora antes que la habilitación de interrupciones:

Código: C#
  1. void exec_COMMAND_START(void){
  2.  
  3.    char sEdge1='u',sEdge2='u',sEdge3='u';
  4.  
  5.    limpia_data_records();
  6.    flagFirstEdge = 0x00;
  7.    Edge1=0;
  8.    Edge2=0;
  9.    Edge3=0;
  10.    if( !input(PIN_B0) ) Edge1=1;
  11.    if( !input(PIN_B1) ) Edge2=1;
  12.    if( !input(PIN_B2) ) Edge3=1;
  13.  
  14.    if(Edge1) sEdge1='H'; else sEdge1='L';
  15.    if(Edge2) sEdge2='H'; else sEdge2='L';
  16.    if(Edge3) sEdge3='H'; else sEdge3='L';
  17.  
  18.    printf("<%c,%c,%c,%c>\r\n",COMMAND_ACK,sEdge1,sEdge2,sEdge3);
  19.  
  20.    timer1_overflows=0x00;
  21.    set_timer1(0);
  22.    enable_interrupts(int_ext);
  23.    enable_interrupts(int_ext1);
  24.    enable_interrupts(int_ext2);
  25.    enable_interrupts(int_timer1);
  26. }
  27.  

Y las interrupciones son tratadas de forma mucho mas concisa de la forma:

Código: C#
  1. #int_ext2
  2. void channel_3_handler(void){
  3.  
  4.    if(flagFirstEdge==0x00){
  5.       flagFirstEdge =0x01;
  6.       timer1_overflows=0x00;
  7.       set_timer1(0);
  8.    }
  9.  
  10.    ChannelEdge[Index] = 'E'+Edge3;
  11.    Overflows[Index]   = timer1_overflows;
  12.    Timer[Index]       = get_timer1();
  13.    ++Index;
  14.    ++Edge3;
  15. }
  16.  

Lo único que he cambiado de la propuesta original de M.Nocturno es que su SENTIDO_INTERRUPCION = ~SENTIDO_INTERRUPCION; lo he implementado como ++SENTIDO_INTERRUPCION ya que al ser un solo bit obtengo los mismo resultados, el de su conmutación de estado.

Por lo demás obtengo los mismos valores salvo el susodicho inicial y ... como la Scarlett O´Hara en Lo que el viento se llevó juro con el puño en alto bajo el inmenso roble con un cielo rojo sangre que tengo que enterarme de por qué pasa esto. Por mis niños que lo tengo que saber.
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: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #4 en: 13 de Septiembre de 2008, 07:56:44 »
A ver si tu desconcierto viene debido a un error de orden superior Don Diego.

Has visto los Data-Errata de ese micro?

http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010300

Comentan algo de los Timers y me suena que hace tiempo leí algo de las interrupciones externas.

Ese micro tiene algunas erratas y es importante que las veas todas.

A ver si hay suerte por este camino (lo que te pasa es tan raro que es lo único que se me ocurre).


Un saludo desde Alicante.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #5 en: 13 de Septiembre de 2008, 09:39:34 »
A ver si hay suerte por este camino (lo que te pasa es tan raro que es lo único que se me ocurre).

Echémosle un vistazo a las *Errata Picae Dolorem Cabezorum Genis:mrgreen:

* Errores en los PIC's que generan dolores de cabeza.
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17502
    • MicroPIC
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #6 en: 13 de Septiembre de 2008, 14:04:15 »
Don Diego, no nos has enseñado la rutina que utilizas para volcar el contenido de los arrays sobre la salida serie. Sé que haces algún cálculo allí dentro para obtener el flanco (L o H) y el canal (1, 2 o 3) a partir del array ChannelEdge.
No creo que esté ahí el problema pero, ¿lo miramos?
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #7 en: 13 de Septiembre de 2008, 14:57:43 »
Un rato más ... que hay cambios y tengo nuevos resultados. Voy a ver si me aclaro algo y os pongo alguna conclusión.
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #8 en: 14 de Septiembre de 2008, 08:05:56 »
20 Horas después de mi anterior post ya tengo las conclusiones. Un cúmulo de errores, desconocimientos, fallo sobre fallo y tiro por que me toca hacen que haya sido difícil sobre todo entender qué es lo que estaba pasando.

Pero ya está solucionado y clarificado:mrgreen: :mrgreen: :mrgreen:

Paso a explicaros los porqueses de todo esto.

Errores:

1º.- El primer error fue el de la misma fuente de datos, el generador de pulsos largaba mas de los 255 flancos entre los tres canales por lo que los últimos sobre-escribían los primeros al desbordar el contador-índice de los Arrays de datos.

2º.- Como bien decía M.Nocturno el poner un printf después de habilitar las interrupciones falseaba los datos al ser el printf extremadamente lento.

3º.- Utilizar la técnica estándar de Array[Indice]=Valor es extremadamente costoso en términos de líneas programa ASM compiladas por el CCS, lo que unido a guardar cuatro valores de este tipo en cada interrupción eran muy largos-lentos y se perdían flancos entre tanto.
 
4º.- El intento de acumular los valores de Channel y Edge en un único valor a guardar hizo todo aún mas lento.

Soluciones:

1º.- Obviamente cambiar el tipo de entero de int8 a int16 del índice para poder acumular mas datos y elever al mismo tiempo subir en el array el número de muestras de 255 a 381, así nos aseguramos que no nos excedemos en el número de datos recogidos.

2º.- Mover o remover todos los procesos no fundamentales con el fin de que al iniciar la toma de datos no se ejecute absolutamente nada accesorio. Asimismo implemento el acceso directo a los registros de cambio de flanco para el disparo de las interrupciones. También se ordena la prioridad de las mismas con #priority y por si acaso (los errores del Timer1 en el 18F4550) he cambiado la base de tiempos del Timer1 al Timer3.

3º.- Para evitar el uso de los índices de un Array he creado una estructura con los cuatro valores, he creado una variable simple con esa estructura que es la que recojo en las interrupciones y después la muevo con un memcpy a su ubicación definitiva en la RAM. Este proceso es infinitamente mas rápido que guardar en el Array las variables una a una porque cada vez hay que recuperar la posición del índice mientras que con el memcpy solo se hace una vez para toda la estructura.

4º.- Con lo anterior este error queda obsoleto y los datos de Channel y Edge se guardan en "crudo" , sin tratar, y solo posteriormente se hará al enviarlos al PC.


Nuevo código Fuente:

Los Defines, Constantes y RAM queda así:

Código: C#
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // DEFINES y CONSTANTES
  4. //
  5. ///////////////////////////////////////////////////////////////////////////////////////////////////
  6.  
  7. #define INTCON2 0xFF1
  8. #bit Edge1 = INTCON2.6
  9. #bit Edge2 = INTCON2.5
  10. #bit Edge3 = INTCON2.4
  11.  
  12. struct tRecord{
  13.  
  14.    char  Channel;
  15.    char  Edge;
  16.    int8  Overflow;
  17.    int16 Timer;
  18. };
  19.  
  20. #define MaxRecords 381
  21.  
  22. ///////////////////////////////////////////////////////////////////////////////////////////////////
  23. //
  24. // R A M
  25. //
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27.  
  28. char   Command='\0';
  29.  
  30. int16  Index;
  31. int8   timer3_overflows;
  32. struct tRecord singleRecord;
  33. struct tRecord Records[MaxRecords];
  34.  

Y estas son las nuevas interrupciones:

Código: C#
  1. // Base de Tiempos //////////////////////////////////////////////////
  2. #int_timer3
  3. void timer3_handler(void){
  4.    ++timer3_overflows;         // One Tick: 0.0833 uS, One Overflow: 5,461333 mS
  5. }
  6.  
  7. // Canal 1 : RB0 ////////////////////////////////////////////////////
  8. #int_ext
  9. void channel_1_handler(void){
  10.  
  11.    singleRecord.Overflow = timer3_overflows;
  12.    singleRecord.Timer    = get_timer3();
  13.    singleRecord.Edge     = Edge1;
  14.    singleRecord.Channel  = '1';
  15.    ++Edge1;
  16.    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  17.    ++Index;
  18. }
  19.  
  20. // Canal 2 : RB1 ////////////////////////////////////////////////////
  21. #int_ext1
  22. void channel_2_handler(void){
  23.  
  24.    singleRecord.Overflow = timer3_overflows;
  25.    singleRecord.Timer    = get_timer3();
  26.    singleRecord.Edge     = Edge2;
  27.    singleRecord.Channel  = '2';
  28.    ++Edge2;
  29.    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  30.    ++Index;
  31.  
  32. }
  33.  
  34. // Canal 3 : RB2 ////////////////////////////////////////////////////
  35. #int_ext2
  36. void channel_3_handler(void){
  37.  
  38.    singleRecord.Overflow = timer3_overflows;
  39.    singleRecord.Timer    = get_timer3();
  40.    singleRecord.Edge     = Edge3;
  41.    singleRecord.Channel  = '3';
  42.    ++Edge3;
  43.    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  44.    ++Index;
  45. }
  46.  

Y por último el nuevo DUMP de datos se realiza de esta forma:

Código: C#
  1. void dump_data_record(int16 nrecord){
  2.  
  3.    int8 dispOverflow;
  4.    
  5.    dispOverflow=Records[nrecord].Overflow-Records[0].Overflow;
  6.  
  7.    printf("<%c,%3Lu,%c,%c,%3u,%5Lu>\r\n",COMMAND_DUMP,nrecord,Records[nrecord].Channel,'0'+Records[nrecord].Edge,dispOverflow,Records[nrecord].Timer);
  8. }
  9.  
  10. void exec_COMMAND_DUMP(void){
  11.  
  12.    int16 i;
  13.  
  14.     for(i=0;i<Index;i++){ // Direct
  15.    // for(i=Index;i<255;--i){ // Reverse
  16.  
  17.       // Corrección de Errores de Recogida de Datos ...
  18.  
  19.       if(i>0){ // Si hay dato anterior
  20.          if(Records[i].Timer<100 && (Records[i].Overflow==Records[i-1].Overflow)){ // Si la recogida del Timer es menor que 100 y ..
  21.             Records[i].Overflow +=1;                                               // el Overflow es igual al anterior el overflow es uno más.
  22.          }
  23.       }
  24.  
  25.       dump_data_record(i);
  26.    }
  27.    printf("Overflows Offset %3u\r\n",Records[0].Overflow);
  28. }
  29.  

Fijaos que en este último trozo de código hay una pequeña corrección para cuando el valor del Timer recogido es pequeño ya que entonces es seguro que la interrupción del Overflow llega tarde y por lo tanto se recoge el valor de overflows anterior.

Resultados:

Y por fin los resultados son completos, correctos y satisfactorios.  :mrgreen:

Os pongo el inicio y el final de una recogida de datos:

<U,L,L,L>                           
<D,  0,3,0,  0,57774>               
<D,  1,1,0,  0,58110>               
<D,  2,1,1,  0,60166>               
<D,  3,1,0,  0,62214>               
<D,  4,1,1,  0,64271>               
<D,  5,1,0,  1,  783>               
<D,  6,1,1,  1, 2839>               
<D,  7,1,0,  1, 4887>               
<D,  8,1,1,  1, 6944>               
<D,  9,1,0,  1, 8992>               
<D, 10,1,1,  1,11049>               
<D, 11,1,0,  1,13097>               
<D, 12,1,1,  1,15153>               
...
...
...
<D,242,1,1,  7,26193>               
<D,243,1,0,  7,28241>               
<D,244,1,1,  7,30297>               
<D,245,1,0,  7,32345>               
<D,246,1,1,  7,34402>               
<D,247,1,0,  7,36450>               
<D,248,1,1,  7,38507>               
<D,249,1,0,  7,40555>               
<D,250,1,1,  7,42611>               
<D,251,1,0,  7,44659>               
<D,252,1,1,  7,46716>               
<D,253,1,0,  7,48764>               
<D,254,1,1,  7,50820>               
<D,255,1,0,  7,52868>               
<D,256,1,1,  7,54925>               
<D,257,3,1,  7,56725>               
Overflows Offset 208                 
                                     
Así que esta primera fase la doy por conclusa.  :mrgreen: :mrgreen: :mrgreen:
« Última modificación: 14 de Septiembre de 2008, 08:53:35 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17502
    • MicroPIC
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #9 en: 14 de Septiembre de 2008, 13:15:30 »
Eres un crack. Me alegro que lo hayas resuelto, y me alegro que hayas aplicado de forma tan elegante el truco del memcpy, porque resulta sumamente interesante.
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #10 en: 14 de Septiembre de 2008, 13:55:56 »
Gracias Manolo. Fíjate que estuve tentado de escribir el interior de las interrupciones en ASM pero después decidí intentar aún algo en C y tiré de las funciones de manejo directo de la memoria.

Este es el código ASM compilado por el CCS con el manejo de Arrays e índice en una interrupción anteriormente:

Código: ASM
  1. .................... // Canal 1 : RB0 ////////////////////////////////////////////////////
  2. 0186:  BCF    FA1.1
  3. 0188:  GOTO   0088
  4. .................... #int_ext
  5. .................... void channel_1_handler(void){
  6. ....................
  7. ....................    Overflows[Index]   = timer3_overflows;
  8. 018C:  CLRF   03
  9. 018E:  MOVLB  4
  10. 0190:  MOVF   x16,W
  11. 0192:  ADDLW  19
  12. 0194:  MOVWF  FE9
  13. 0196:  MOVLW  01
  14. 0198:  ADDWFC 03,W
  15. 019A:  MOVWF  FEA
  16. 019C:  MOVFF  19,FEF
  17. ....................    Timer[Index]       = get_timer3();
  18. 01A0:  BCF    FD8.0
  19. 01A2:  RLCF   x16,W
  20. 01A4:  MOVWF  02
  21. 01A6:  CLRF   03
  22. 01A8:  RLCF   03,F
  23. 01AA:  MOVF   02,W
  24. 01AC:  ADDLW  18
  25. 01AE:  MOVWF  FE9
  26. 01B0:  MOVLW  02
  27. 01B2:  ADDWFC 03,W
  28. 01B4:  MOVWF  FEA
  29. 01B6:  MOVF   FB2,W
  30. 01B8:  MOVFF  FB3,03
  31. 01BC:  MOVWF  FEF
  32. 01BE:  MOVFF  03,FEC
  33. 01C2:  MOVF   FED,F
  34. ....................    ChannelEdge[Index] = 'A'+Edge1;
  35. 01C4:  CLRF   03
  36. 01C6:  MOVF   x16,W
  37. 01C8:  ADDLW  1A
  38. 01CA:  MOVWF  FE9
  39. 01CC:  MOVLW  00
  40. 01CE:  ADDWFC 03,W
  41. 01D0:  MOVWF  FEA
  42. 01D2:  MOVLW  00
  43. 01D4:  BTFSC  FF1.6
  44. 01D6:  MOVLW  01
  45. 01D8:  ADDLW  41
  46. 01DA:  MOVWF  FEF
  47. ....................    ++Index;
  48. 01DC:  INCF   x16,F
  49. ....................    ++Edge1;
  50. 01DE:  BTG    FF1.6
  51. .................... }
  52.  

Y este los mismo con el memcpy() :

Código: ASM
  1. .................... // Canal 1 : RB0 ////////////////////////////////////////////////////
  2. 0186:  BCF    FA1.1
  3. 0188:  GOTO   0088
  4. .................... #int_ext
  5. .................... void channel_1_handler(void){
  6. ....................
  7. ....................    singleRecord.Overflow = timer3_overflows;
  8. *
  9. 01AE:  MOVFF  1C,1F
  10. ....................    singleRecord.Timer    = get_timer3();
  11. 01B2:  MOVF   FB2,W
  12. 01B4:  MOVFF  FB3,03
  13. 01B8:  MOVWF  20
  14. 01BA:  MOVFF  03,21
  15. ....................    singleRecord.Edge     = Edge1;
  16. 01BE:  CLRF   1E
  17. 01C0:  BTFSC  FF1.6
  18. 01C2:  INCF   1E,F
  19. ....................    singleRecord.Channel  = '1';
  20. 01C4:  MOVLW  31
  21. 01C6:  MOVWF  1D
  22. ....................    ++Edge1;
  23. 01C8:  BTG    FF1.6
  24. ....................    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  25. 01CA:  MOVFF  1B,7B7
  26. 01CE:  MOVFF  1A,7B6
  27. 01D2:  MOVLB  7
  28. 01D4:  CLRF   xB9
  29. 01D6:  MOVLW  05
  30. 01D8:  MOVWF  xB8
  31. 01DA:  MOVLB  0
  32. 01DC:  RCALL  018C
  33. 01DE:  MOVFF  02,03
  34. 01E2:  MOVF   01,W
  35. 01E4:  ADDLW  22
  36. 01E6:  MOVLB  7
  37. 01E8:  MOVWF  xAC
  38. 01EA:  MOVLW  00
  39. 01EC:  ADDWFC 02,W
  40. 01EE:  MOVWF  xAD
  41. 01F0:  MOVWF  FEA
  42. 01F2:  MOVFF  7AC,FE9
  43. 01F6:  CLRF   FE2
  44. 01F8:  MOVLW  1D
  45. 01FA:  MOVWF  FE1
  46. 01FC:  MOVLW  05
  47. 01FE:  MOVWF  01
  48. 0200:  MOVFF  FE6,FEE
  49. 0204:  DECFSZ 01,F
  50. 0206:  BRA    0200
  51. ....................    ++Index;
  52. 0208:  INCF   1A,F
  53. 020A:  BTFSC  FD8.2
  54. 020C:  INCF   1B,F
  55. .................... }
  56.  
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17502
    • MicroPIC
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #11 en: 14 de Septiembre de 2008, 14:18:12 »
Te tiro otra sugerencia, por si te apetece recogerla.
1.- Guarda en tres variables el estado inicial de los tres pines de entrada, o sea, el que te permitirá saber qué flanco será el siguiente en llegar y levantar la interrupción.
2.- Deja de almacenar el flanco actual en cada interrupción. Se simplifica tu estructura en un byte (20% de la misma) y el código compilado en 4 instrucciones.
3.- A la hora de hacer el dump reconstruyes a partir del flanco inicial el resto de los flancos cambiándolos cada vez que se haya leído una captura en un pin.
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #12 en: 14 de Septiembre de 2008, 14:19:10 »
Hummm ... imagino que jugando con un único puntero e incrementándolo en sizeof(singleRecord) y asignando valores uno a uno se podría hacer aún mas corto y eficaz.
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado jeremylf

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1338
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #13 en: 18 de Septiembre de 2008, 02:05:31 »
Wow.

Cuanta precicion, eso me gusta. Pero, el echo de que el pic corra a 0.08333....... us te quita algo de precicion creo yo, y es una pena y algo que siempre tendre como un bichito en la cabeza  :D No es algo como corriendo al pic a 4 o 20 mhz donde si es exacto.

Habria que probar con los pic32 que corren a 80mhz = 0.0125us EXACTO!  :lol:

Aun asi, te quedo de lujo!
Un saludo.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5415
    • Picmania by Redraven
Re: Detectando Flancos de subida (y de bajada) con un 18F4550
« Respuesta #14 en: 21 de Septiembre de 2008, 05:23:23 »
Te tiro otra sugerencia, por si te apetece recogerla.
1.- Guarda en tres variables el estado inicial de los tres pines de entrada, o sea, el que te permitirá saber qué flanco será el siguiente en llegar y levantar la interrupción.
2.- Deja de almacenar el flanco actual en cada interrupción. Se simplifica tu estructura en un byte (20% de la misma) y el código compilado en 4 instrucciones.
3.- A la hora de hacer el dump reconstruyes a partir del flanco inicial el resto de los flancos cambiándolos cada vez que se haya leído una captura en un pin.

Implementado con éxito tu indicación.   :mrgreen:

Así en la RAM me defino tres variables int1

Código: C#
  1. int1   iEdge1, iEdge2, iEdge3;
  2.  

Que inicializo con los valores contrarios léidos con input por cada canal

Código: C#
  1.    ext_int_edge(0,H_TO_L);
  2.    ext_int_edge(1,H_TO_L);
  3.    ext_int_edge(2,H_TO_L);
  4.    if( !input(PIN_B0) ){ ext_int_edge(0,L_TO_H); }
  5.    if( !input(PIN_B1) ){ ext_int_edge(1,L_TO_H); }
  6.    if( !input(PIN_B2) ){ ext_int_edge(2,L_TO_H); }
  7.  
  8.    iEdge1=Edge1;
  9.    iEdge2=Edge2;
  10.    iEdge3=Edge3;
  11.  

Y en las interrupciones #int_ext, #int_ext1 e #int_ext2 dejo de guardar este dato centrándome solo en el canal y los datos de Timer

Código: C#
  1. #int_ext
  2. void channel_1_handler(void){
  3.  
  4.    singleRecord.Overflow = timer3_overflows;
  5.    singleRecord.Timer    = get_timer3();
  6.    singleRecord.Channel  = '1';
  7.    ++Edge1;
  8.    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  9.    ++Index;
  10. }
  11. ...
  12.  

Pero al hacerp el DUMP lo reconstruyo en función del estado inicial y conmutando sucesivamente de forma independiente por cada canal antes de utilizar el printf

Código: C#
  1.    // Computa Edge en función de iEdgex
  2.    sEdge='u';
  3.    switch(Records[nrecord].Channel){
  4.       case '1': sEdge='0'+iEdge1; ++iEdge1; break;
  5.       case '2': sEdge='0'+iEdge2; ++iEdge2; break;
  6.       case '3': sEdge='0'+iEdge3; ++iEdge3; break;
  7.    }
  8.  

Con esto y al compilar una interrupción queda reducida a

Código: ASM
  1. .................... // Canal 1 : RB0 ////////////////////////////////////////////////////
  2. .................... #int_ext
  3. .................... void channel_1_handler(void){
  4. ....................
  5. 018C:  MOVFF  1C,1E ....................    singleRecord.Overflow = timer3_overflows;
  6. 0190:  MOVF   FB2,W ....................    singleRecord.Timer    = get_timer3();
  7. 0192:  MOVFF  FB3,03
  8. 0196:  MOVWF  1F
  9. 0198:  MOVFF  03,20 ....................    singleRecord.Channel  = '1';
  10. 019C:  MOVLW  31
  11. 019E:  MOVWF  1D
  12. 01A0:  BTG    FF1.6 ....................    ++Edge1;
  13. 01A2:  RLCF   1A,W  ....................    memcpy(&Records[Index],&singleRecord,sizeof(singleRecord));
  14. 01A4:  MOVWF  02
  15. 01A6:  RLCF   1B,W
  16. 01A8:  MOVWF  03
  17. 01AA:  RLCF   02,F
  18. 01AC:  RLCF   03,F
  19. 01AE:  MOVLW  FC
  20. 01B0:  ANDWF  02,F
  21. 01B2:  MOVF   02,W
  22. 01B4:  ADDLW  21
  23. 01B6:  MOVLB  6
  24. 01B8:  MOVWF  x4F
  25. 01BA:  MOVLW  00
  26. 01BC:  ADDWFC 03,W
  27. 01BE:  MOVWF  x50
  28. 01C0:  MOVWF  FEA
  29. 01C2:  MOVFF  64F,FE9
  30. 01C6:  CLRF   FE2
  31. 01C8:  MOVLW  1D
  32. 01CA:  MOVWF  FE1
  33. 01CC:  MOVLW  04
  34. 01CE:  MOVWF  01
  35. 01D0:  MOVFF  FE6,FEE
  36. 01D4:  DECFSZ 01,F
  37. 01D6:  BRA    01D0
  38. 01D8:  INCF   1A,F ....................    ++Index;
  39. 01DA:  BTFSC  FD8.2
  40. 01DC:  INCF   1B,F
  41. .................... }
  42.  

Y ahora la pregunta del millón de un profundo desconocedor del ASM ... ¿cada instrucción ocupa 4 ciclos de reloj? o ¿hay instrucciones de mas y/o menos ciclos?. Lo digo para calcular exactamente cuanto ocupa en tiempo la ejecución de la interrupción.

Si la respuesta a la primera pregunta es SI y NO a la segunda y contando cuento 36 instrucciones a 4 ciclos de reloj cada una hacen un total de ciclos de 144 ciclos y con el PIC corriendo a 48 Mhz con cada ciclo a 1/48000000 resulta que la interrupción se ejecuta en 0,000003 (en segundos) o sea 3 microsegundos.

¿Yes OR Not Yes?  :shock:



« Última modificación: 21 de Septiembre de 2008, 05:32:04 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania