TODOPIC
Bienvenido(a), Visitante. Por favor, ingresa o regístrate.
¿Perdiste tu email de activación?
03 de Septiembre de 2010, 05:27:51

Ingresar con nombre de usuario, contraseña y duración de la sesión
Buscar:     Búsqueda Avanzada
257111 Mensajes en 28437 Temas por 27916 Usuarios
Último usuario: zororyuzaki
* Inicio Ayuda Buscar Calendario Ingresar Registrarse
Buscar en TodoPIC
+  TODOPIC
|-+  Microcontroladores PIC
| |-+  Lenguaje C para microcontroladores PIC (Moderadores: Modulay, pikman, pocher, vszener, Suky)
| | |-+  Detectando Flancos de subida (y de bajada) con un 18F4550
0 Usuarios y 1 Visitante están viendo este tema. « anterior próximo »
Páginas: [1] 2 Marcar como favorito Imprimir
Autor Tema: Detectando Flancos de subida (y de bajada) con un 18F4550  (Leído 1650 veces)
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« : 10 de Septiembre de 2008, 05: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.  Mr. Green

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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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.  redhot

<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?  Shocked Shocked Shocked
« Última modificación: 10 de Septiembre de 2008, 06:16:43 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #1 : 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.




En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #2 : 11 de Septiembre de 2008, 08:25:02 »

 Shocked Shocked Shocked Shocked Shocked Shocked

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

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) lol lol lol

En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #3 : 13 de Septiembre de 2008, 05:46:04 »

 Shocked Shocked Shocked Shocked Shocked

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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Azicuetano
Moderadores
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 1012



WWW
« Respuesta #4 : 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.
En línea

RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #5 : 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 GenisMr. Green

* Errores en los PIC's que generan dolores de cabeza.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #6 : 13 de Septiembre de 2008, 02: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?
En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #7 : 13 de Septiembre de 2008, 02: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.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #8 : 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 clarificadoMr. Green Mr. Green Mr. Green

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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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.  Mr. Green

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.  Mr. Green Mr. Green Mr. Green
« Última modificación: 14 de Septiembre de 2008, 08:53:35 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #9 : 14 de Septiembre de 2008, 01: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.
En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #10 : 14 de Septiembre de 2008, 01: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
GeSHi (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
GeSHi (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.  
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #11 : 14 de Septiembre de 2008, 02: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.
En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #12 : 14 de Septiembre de 2008, 02: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.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
jeremylf
Colaborador
PIC24F
*****
Desconectado Desconectado

Sexo: Masculino
Peru Peru

Mensajes: 587



« Respuesta #13 : 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  lol 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!  Laughing

Aun asi, te quedo de lujo!
Un saludo.
En línea
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #14 : 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.   Mr. Green

Así en la RAM me defino tres variables int1

Código
GeSHi (csharp):
  1. int1   iEdge1, iEdge2, iEdge3;
  2.  

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

Código
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (csharp):
  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
GeSHi (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?  Shocked



« Última modificación: 21 de Septiembre de 2008, 05:32:04 por RedPic » En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #15 : 21 de Septiembre de 2008, 05:31:21 »

Diego, mira la tabla 26-2 de la Datasheet: PIC18FXXXX INSTRUCTION SET

Hay una columna al lado de cada instrucción que dice "Cycles".

De todas formas, para medir el tiempo que toma la interrupción te recomiendo otro método. Carga tu programa en MPLAB, le das al debugger paso a paso y con la ventana Stopwatch tienes el tiempo exacto tanto en ciclos de reloj como en microsegundos (si has configurado bien la frecuencia de trabajo).
En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #16 : 21 de Septiembre de 2008, 05:33:22 »

Ya. My gozo in the pozo.  Confused
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #17 : 21 de Septiembre de 2008, 06:00:26 »

¿Qué t'ha pasao?
En línea

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

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #18 : 21 de Septiembre de 2008, 06:12:06 »

Nada, nada ... que el cálculo de 4 ciclos por cuarto-y-mitad de instrucción es una cagada. Me pongo a contar ciclos por tipo de instrucción según tabla y sumo de verdad (el MPLAB no es mi fuerte) ...  Mr. Green
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #19 : 21 de Septiembre de 2008, 06:45:18 »

Pues salvo error u omisión mi cálculo anterior no es tan descaminado como parecíame al principio ...

He colocado a la izquierda de cada instrucción el número de ciclos de intrucciones que consume ...

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


Que sumadas hacen un total de 46 ciclos de intrucción, y como cada uno de ellos consume 4 ciclos de reloj tenemos un total de 46 * 4 = 184 ciclos de reloj. Sólo 40 mas que en el calculo anterior.

Hay dos instrucciones, DECFSZ y  BTFSC, que el Datasheet dice 1 ciclo (2 ó 3) y como no indica cuando ocupa cuantos ciclos he puesto que siempre ocupa 3.

Así que 184 * 1 / 48000000 = 3,8333 microsegundos. Casi 4 microsegundos que no está nada mal (para los tiempos que estoy sensando que son del orden de los 250 microsegundos)  Mr. Green Mr. Green Mr. Green

Un poné.

Muchas gracias manuelo.
En línea

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

 En línea
Páginas: [1] 2 Imprimir 
« anterior próximo »
Ir a:  

Impulsado por MySQL Impulsado por PHP Powered by SMF 1.1.11 | SMF © 2006-2008, Simple Machines LLC XHTML 1.0 válido! CSS válido!
Página creada en 0.819 segundos con 22 consultas.