Está en la página 1de 24

Midiendo un pulso. 1 Parte. Tiempo en Alto con INTEXT A.

- Conceptos involucrados: Los pulsos corren alocados entre dos estados conocidos: Alto y Bajo. Es lo que conocemos como un Tren de Pulsos. El bajo (Low) cuando la tensin aplicada al Pin est cerca de GND y el otro, el Alto (High), cuando se parece a VCC. A todos los efectos para nuestras aplicaciones vamos a considerarlos como GND y VCC respectivamente. Cuando el estado pasa de Bajo a Alto decimos que nos ha llegado un Flanco de Subida (Raising Edge). Cuando por el contrario el estado pasa de Alto a bajo entonces decimos que nos llega un Flanco de Bajada (Falling Edge). Un Pulso (Pulse) es lo que ocurre entre uno y otro Flancos de Subida sucesivos (o de Bajada). El Tiempo T que transcurre ente dos flancos sucesivos de subida (o de bajada) es lo que conocemos como Periodo del Pulso (Period), o sea: lo que dura un pulso completo. Cada T segundos ( milisegundos, microsegundos) tenemos un nuevo pulso completo. Como vemos, cada pulso tiene un Tiempo en Alto (High Time) y un Tiempo en estado Bajo (Low Time). La suma de los dos es el Periodo T del pulso. Al Tiempo que permanece un pulso en Alto que es lo que vamos a llamar Ancho del Pulso (Pulse Width) y es el tiempo que pretendemos medir. W en el esquema inferior. B.- Tcnica a Aplicar: Para medir el ancho W del pulso que permanece en Alto vamos a utilizar dos recursos de los que disponen la inmensa mayoria de los PIC's, tanto los de la serie 18F como los 16F: El TIMER1 y la Interrupcin Externa por RB0. * El TIMER1 es un contador de 16 bits que se incrementa cada 4 ciclos de Reloj (FOSC /4). A este tiempo le vamos a llamar Tick de TIMER1. Cuando el TIMER1 alcanza el valor 0xFFFF contina de nuevo por 0x0000, y as hasta el infinito y mas all. (Cuando pasa de 0xFFFF a 0x0000 genera una Interrupcin por Desbordamiento de Timer pero que no vamos a utilizar en este ejemplo, as que no vamos a entrar en cmo funciona). Si el cristal de cuarzo con el calzamos nuestro PIC es de 20 Mhz, por ejemplo, entonces cada 4 * 1/20.000.000 = 0,0000002 segundos (0.2 microsegundos) se produce un Tick de TIMER1.

De igual forma una vuelta completa del TIMER1, desde 0x0000 hasta 0xFFFF (65536 pasos), ocupa un tiempo total de 0,0000002 * 65.536 = 0,0131072 segundos (13,1072 milisegundos) * La Interrupcin Externa por RB0, conocida como Int_Ext consiste en una peticin de interrupcin que se produce al recibir por RB0 un flanco de subida o bajada determinado, que es configurable por nuestro firmware. Podemos as fijar un tipo de flanco a detectar y escribir un cierto cdigo para ejecutarlo cuando ese tipo de flanco, subida o bajada, es detectado. Es lo que conocemos como Peticin de Servicio por Interrupcin (Interrupt Service Request ISR) En este cdigo ISR escrito para tratar la interrupcin externa por RB0 podemos mantener o cambiar el flanco a detectar, cambindolo del de Subida al de Bajada o viceversa y leer o escribir el valor de TIMER1 que en ese momento tiene el contador. * Lo que vamos a realizar es: 1.- Configuraremos la Int_Ext por RB0 para detectar inicialmente el Flanco de Subida. 2.- Al llegarnos este Flanco de Subida guardaremos el valor en ese momento de TIMER1 y ... 3.- Cambiaremos la configuracin de la Int_Ext por RB0 para detectar el siguiente Flanco de Bajada. 4.- Cuando nos llegue el Flanco de Bajada guardaremos de nuevo el valor de TIMER1 y ... 5.- Volveremos a configurar la Int_Ext por RB0 para detectar de nuevo un Flanco de Subida. Y ... 6.- Con estos dos valores de TIMER1 tendremos, expresados en Tick's de TIMER1, restando el segundo del primero, el tiempo que ha permanecido en alto el Pulso. Multiplicando dicho nmero de Tick's de TIMER1 por el tiempo que dura cada Tick (dependiente del cristal que usemos) tendremos el Tiempo W que estamos buscando. C.- Implementacin en C: Para implementar nuestro Cdigo en C vamos a centrarnos en los puntos que hemos descrito en la seccin anterior. Para configurar inicialmente el flanco de subida a detectar utilizaremos: Cdigo
GeSHi (c): 1. ext_int_edge(0,L_TO_H); de 1er flanco de subida 2. flagToggleFlanco = 0; Flag para cambiar de flanco 3. enable_interrupts(int_ext); interrupciones necesarias 4. enable_interrupts(global); 5. // Configuro captura // inicializo el // Habilito las

Hemos aadido la variable int1 flagToggleFlanco debido a que una vez establecido el flanco a detectar no tenemos oportunidad de saber qu flanco es el que estamos esperando, as si flagToggleFlanco tiene un valor de 0 es que estamos esperando el de Subida y si por el contrario flagToggleFlanco tiene un valor de 1 entonces es el de bajada el que esperamos. Con esta configuracin inicial podemos ya escribir nuestra rutina ISR: Cdigo
GeSHi (c): 1. #int_ext 2. void handle_ext_int(){ 3. 4. if(flagToggleFlanco==0){ Flanco de Subida 5. 6. t1=get_timer1(); valor de TMR1 al Flanco de Subida 7. ext_int_edge(0,H_TO_L); capturar siguiente flanco de Bajada 8. flagToggleFlanco=1; siguiente flanco ser de Bajada 9. 10. } else { Flanco de Bajada 11. 12. t2=get_timer1(); valor de TMR1 al Flanco de Bajada 13. ext_int_edge(0,L_TO_H); capturar siguiente flanco de subida 14. flagToggleFlanco=0; siguiente flanco ser de Subida 15. set_timer1(0); 16. if(flagHayDatos==0){ anteriores han sido procesados ... 17. flagHayDatos=1; hay nuevos datos de flancos para calcular 18. } 19. } 20. } 21.

// He recibido // Guardo en t1 el // Configuro para // Indico que el // He recibido // Guardo en t2 el // Configuro para // Indico que el // Reinicio TMR1 // Si los datos // Indico que ya

Fijaos que en la rutina ISR anterior no realizamos ningn clculo. Aplicamos aqui el principio fundamental de que "dentro de una rutina de interrupcin haz lo debas y abandnala lo antes que puedas". Asi que si tenemos valores vlidos de T1 y T2 entonces solo hacemos que flagHayDatos valga 1 y ya trataremos estos valores dentro de main(). Cdigo
GeSHi (c): 1. if(flagHayDatos==1){ hay datos de flancos ... // Detecto que ya

2. 3. 4. 5. 6. 7. 8. 9.

if(t2 > t1){ estoy en la misma vuelta de TMR1 tt = t2 - t1; de TMR1 el tiempo entre flancos st = uSxTick * tt; tiempo. flagHayTransmitir=1; nuevo valor para transmitir } flagHayDatos=0; sido procesados los datos. }

// Compruebo que // Calculo en Tick's // Calculo en uS el // Indico que tengo // Indico que ya han

Cuando flagHayDatos es 1 podemos proceder a calcular los Ticks transcurridos entre un flanco y otro. Fijaos que preguntamos si T2 es mayor que T1. Esto es debido a una limitacin que nos impone el no tener en cuenta vueltas completas de TIMER1 para realizar nuestros clculos: T2 y T1 deben estar dentro de una misma vuelta de TIMER2, o sea que el pulso en alto debe ser menor que 13,1072 milisegundos. Notad que cuando detectamos el segundo flanco volvemos a poner TIMER1 a cero para recomenzar siempre cerca del inicio de la cuenta de TIMER1 y evitar asi su desborde. Para computar tiempos de pulso mayores 13,1072 milisegundos solo habra que computar tambien cuantas veces se desborada completo TIMER1 y realizar los clculos aritmticos correspondientes. Al igual que hacamos en la Rutina ISR en que no calculbamos sino que solo indicbamos que podamos calcular, aqu una vez realizados los calculos de Ticks y su equivalencia en segundos no los transmitimos, slo indicamos que hay datos para transmitir mediante poner a 1 flagHayTransmitir. Para transmitir el ltimo valor computado vamos a implementar una recepcin va serie RS232 que al recibir el comando 'T' nos enva el ltimo valor correcto registrado: Cdigo
GeSHi (c): 1. 2. 3. 4. 5. 6. 7. 8. 9. if(Command!=0x00){ // Si he recibido un comando va Serie ... if(Command=='T'){ // Si el comando es 'T' (Transmite) ... if(flagHayTransmitir==1){ // Si hay algo pendiente de transmitir ... printf("Ticks %Lu - %Lu = %Lu = %3.1fuS \r\n",t2,t1,tt,st); flagHayTransmitir=0; // Indico que ya he transmitido lo pendiente. } } Command=0x00; // Indico que ya he procesado el comando. }

10.

Con todo esto el programa completo para nuestro Detector de Tiempo en Alto de un Pulso queda as: Cdigo
GeSHi (c): 1. //////////////////////////////////////////////////////////////// //////////////////// 2. // 3. // Midiendo_un_pulso_1_Width_High.c 4. // 5. // SERIE: "Tcnicas en C" para el Foro TODOPIC 6. // 7. // (c) 10.2006 by RedPic 8. // 9. // Propsito: Medir el tiempo que permanece un pulso en alto 10. // 11. // Condiciones de Test: Inyeccin por RB0 de una seal de 2 Khz (0.5 ms de periodo) 12. // 13. // Tcnica Empleada: Detectar mediante la Interrupcin Externa por RB0 14. // un flanco de subida de un pulso, guardar el estado 15. // de TMR1, detectar a continuacin el siguiente 16. // flanco de bajada, guardar el nuevo estado de TMR1, 17. // realizar las substraccin de ambos para obtener el 18. // tiempo que permanece en alto y transmitir mediante 19. // el puerto RS232 a peticin. 20. // 21. /////////////////////////////////////////////////////////////// ///////////////////// 22. 23. #include <18f4550.h> 24. #fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NO DEBUG,NOWRT,NOVREGEN 25. #use delay(clock=20000000) 26. 27. #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) 28. 29. /////////////////////////////////////////////////////////////// ///////////////////// 30. // 31. // Defines y Constantes 32. // 33. /////////////////////////////////////////////////////////////// ///////////////////// 34. 35. #define LED PIN_E0 // Defino el Pin del Led 36. #define FLASH Output_Toggle(LED) // Defino la funcion Flash de monitor

37. 38. float const uSxTick = 0.2; // Microsegundos por Tick de TMR1 a 20 Mhz 39. 40. /////////////////////////////////////////////////////////////// ///////////////////// 41. // 42. // Variables en RAM 43. // 44. /////////////////////////////////////////////////////////////// ///////////////////// 45. 46. char cRec=0x00; // ltimo caracter recibido via serie 47. char Command=0x00; // Comando a procesar 48. int1 flagToggleFlanco=0; // Flag para cambiar de flanco 49. int16 t1=0x00,t2=0x00,tt=0x00; // Variables para guardar estados de ... 50. float st=0.0; // TMR1 en cada flanco y hacer la resta 51. int1 flagHayDatos=0; // Flag para indicar que ya hay datos de .. 52. // dos flancos (de subida y bajada) 53. int1 flagHayTransmitir=0; // Flag para indicar que hay datos para ... 54. // Transmitir al PC. 55. 56. /////////////////////////////////////////////////////////////// ///////////////////// 57. // 58. // Interrupcin por Recepcin Serie RS232 59. // 60. /////////////////////////////////////////////////////////////// ///////////////////// 61. 62. #int_rda 63. void handle_rda_int(){ 64. 65. if(kbhit()){ // Si hay algo pdte de recibir ... 66. cRec=getc(); // lo recibo sobre cRec ... 67. if(cRec!=0x00){ // Si es distinto de \0 ... 68. Command=ToUpper(cRec); // cargo cRec sobre Command para procesarlo 69. } // pasndolo a Maysculas para no confundir. 70. } 71. } 72. 73. /////////////////////////////////////////////////////////////// ///////////////////// 74. // 75. // Interrupcin por Externa por Cambio de Flanco en RB0 76. //

77. /////////////////////////////////////////////////////////////// ///////////////////// 78. 79. #int_ext 80. void handle_ext_int(){ 81. 82. if(flagToggleFlanco==0){ // He recibido Flanco de Subida 83. 84. t1=get_timer1(); // Guardo en t1 el valor de TMR1 al Flanco de Subida 85. ext_int_edge(0,H_TO_L); // Configuro para capturar siguiente flanco de Bajada 86. flagToggleFlanco=1; // Indico que el siguiente flanco ser de Bajada 87. 88. } else { // He recibido Flanco de Bajada 89. 90. t2=get_timer1(); // Guardo en t2 el valor de TMR1 al Flanco de Bajada 91. ext_int_edge(0,L_TO_H); // Configuro para capturar siguiente flanco de subida 92. flagToggleFlanco=0; // Indico que el siguiente flanco ser de Subida 93. set_timer1(0); // Reinicio TMR1 94. if(flagHayDatos==0){ // Si los datos anteriores han sido procesados ... 95. flagHayDatos=1; // Indico que ya hay nuevos datos de flancos para calcular 96. } 97. } 98. FLASH; // Reproduzco la entrada mediante un LEd en E0; 99. } 100. 101. void main() { 102. 103. ////////////////////////////////////////// INICIALIZACIONES GENERALES 104. 105. delay_ms(333); // Espero a que todo se estabilice e ... 106. disable_interrupts(global); // Inicializo el Micro y ... 107. disable_interrupts(int_timer1); // deshabilitando todo lo no necesario ... 108. disable_interrupts(int_rda); 109. disable_interrupts(int_ext); 110. disable_interrupts(int_ext1); 111. disable_interrupts(int_ext2); 112. setup_adc_ports(NO_ANALOGS); 113. setup_adc(ADC_OFF); 114. setup_spi(FALSE); 115. setup_psp(PSP_DISABLED); 116. setup_counters(RTCC_INTERNAL,RTCC_DIV_2); 117. setup_timer_0(RTCC_OFF); 118. setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); 119. setup_timer_2(T2_DISABLED,0,1); 120. setup_timer_3(T3_DISABLED); 121. setup_comparator(NC_NC_NC_NC);

122. setup_vref(FALSE); 123. port_b_pullups(FALSE); 124. delay_ms(333); 125. 126. /////////////////////////////////////////// INICIALIZACIONES PERTINENTES A LA APLICACION 127. 128. set_tris_c(0b10000000); // Habilito como entrada RC7 para canal RS232 129. 130. ext_int_edge(0,L_TO_H); // Configuro captura de 1er flanco de subida 131. flagToggleFlanco = 0; // inicializo el Flag para cambiar de flanco 132. 133. enable_interrupts(int_rda); // Habilito las interrupciones necesarias 134. enable_interrupts(int_ext); 135. enable_interrupts(global); 136. 137. printf("\r\nMidiendo un pulso : Width High\r\n"); 138. printf("By Redpic para Foro TODOPIC\r\n\n"); 139. 140. do { 141. 142. if(flagHayDatos==1){ // Detecto que ya hay datos de flancos ... 143. if(t2 > t1){ // Compruebo que estoy en la misma vuelta de TMR1 144. tt = t2 - t1; // Calculo en Tick's de TMR1 el tiempo entre flancos 145. st = uSxTick * tt; // Calculo en uS el tiempo. 146. flagHayTransmitir=1; // Indico que tengo nuevo valor para transmitir 147. } 148. flagHayDatos=0; // Indico que ya han sido procesados los datos. 149. } 150. if(Command!=0x00){ // Si he recibido un comando va Serie ... 151. if(Command=='T'){ // Si el comando es 'T' (Transmite) ... 152. if(flagHayTransmitir==1){ // Si hay algo pendiente de transmitir ... 153. 154. printf("Ticks %Lu - %Lu = %Lu = %3.1fuS \r\n",t2,t1,tt,st); 155. 156. flagHayTransmitir=0; // Indico que ya he transmitido lo pendiente. 157. } 158. } 159. Command=0x00; // Indico que ya he procesado el comando. 160. } 161. } while (TRUE); 162. } 163.

Seguimos dndole vueltas al Tema de Capturar el ancho de un pulso ... En Midiendo un pulso. 1 Parte. Tiempo en Alto con INTEXT hemos utilizado la tcnica de recoger el valor de TMR1 cuando ocurre la interrupcin externa por RB0, sea en el flanco de cada, sea en el de subida. Con los valores de ambos flancos y simplemente restando el uno del otro tenemos el valor del ancho del pulso. Hay otra manera de hacer exactamente lo mismo de una forma absolutamente similar: Utilizando el mdulo CCP del PIC funcionando en modo Capture. A.- Conceptos involucrados: Los conceptos son exactamente los mismos que los descritos en la parte anterior por lo que os ruego que consultis si os es necesario: Midiendo un pulso. 1 Parte B.- Tcnica a Aplicar: El mdulo hardware CCP del PIC en configuracin Capture realiza de forma automtica (por hardware) lo que implementamos en nuestro anterior Tcnica en C mediante la interrupcin externa por RB0. Cuando activamos el mdulo CCP, le configuramos el flanco que deseamos que lo dispere, subida o bajada, automticamente cada vez que se nos presente dicho flanco en el pin correspondiente se copia el valor de TMR1, de 16 bits, en la pareja de registros CCPRL y CCPRH. Cada vez que llega un flanco tenemos en CCPR el valor en ese momento de TMR1. Si adems habilitamos la Interrupcin CCP del PIC se producir adems una Peticin de Servicio para esta interrupcin cada vez que nuestro esperado flanco se presente en el pin correspondiente. En esta rutina de interrupcin podremos as cambiar el modelo de flanco a utilizar para el siguiente disparo del CCP, cambindolo ahora al flanco de bajada, y as en el siguiente disparo tendremos en CCPR el nuevo valor de TIMER1 con lo restando ste del anterior tendremos el ancho de pulso en High que deseamos. Como vis es absolutamente identico al mtodo anterior pero sin tener que recoger "a mano" el valor de TMR1. C.- Implementacin en C: Nota Importante: En mi ejemplo utilizo el 18F4550 de la RRBOARD2 usando del mdulo CCP2, de los dos que tiene este PIC, configurndolo adems para que CCP2 en lugar de estar en RC1 como originalmente est configurado y que estoy usando para otras cosas, se multiplexe por RB3 que lo tengo libre. Esto ltimo se consigue en el 18F4550 usando el fuse CCP2B3. Para configurar inicialmente el flanco de subida a detectar utilizaremos:

Cdigo
GeSHi (c): 1. setup_ccp2(CCP_CAPTURE_RE); flanco de subida 2. flagToggleFlanco = 0; cambiar de flanco 3. enable_interrupts(int_ccp2); 4. enable_interrupts(global); 5. // Configuro captura de 1er // inicializo el Flag para

Nuestra rutina ISR para CCP quedara como sigue: Cdigo


GeSHi (c): 1. #int_ccp2 2. void handle_ccp2_int(){ 3. if(flagToggleFlanco==0){ // He recibido Flanco de Subida 4. t1=CCP_2; // Guardo la captura del CCP2 al Flanco de Subida 5. setup_ccp2(CCP_CAPTURE_FE); // Configuro para capturar siguiente flanco de Bajada 6. flagToggleFlanco=1; // Indico que el siguiente flanco ser de Bajada 7. 8. } else { // He recibido Flanco de Bajada 9. 10. t2=CCP_2; // Guardo en t2 la nueva captura de CCP2 al Flanco de Bajada 11. setup_ccp2(CCP_CAPTURE_RE); // Configuro para capturar siguiente flanco de subida 12. flagToggleFlanco=0; // Indico que el siguiente flanco ser de Subida 13. if(flagHayDatos==0){ // Si los datos anteriores han sido procesados ... 14. flagHayDatos=1; // Indico que ya hay nuevos datos de flancos para calcular 15. } 16. } 17.

El resto de la implementacin en C de esta Tcnica es identica a la mostrada en Midiendo un pulso. 1 Parte. Tiempo en High con Int_Ext D.- Ejemplo funcionando:

La seal inyectada podis verla en :

Bueno, y esto es todo por hoy amigos.

Y por fin le damos una vuelta de tuerca al Tema de Capturar el ancho de un pulso ... Por indicacin del amigo filth ampliamos lo que hemos visto en Midiendo un pulso. 1 Parte. Tiempo en Alto con INTEXT y en Midiendo un pulso. 2 Parte. Tiempo en Alto con INTCCP. En ambas hemos utilizado la tcnica de recoger el valor de TMR1 cuando ocurre o la interrupcin externa por RB0 o la interrupcion CCP, sea en el flanco de cada, sea en el de subida. Con los valores de ambos flancos y simplemente restando el uno del otro tenemos el valor del ancho del pulso. Hay otra manera de hacer exactamente lo mismo utilizando una tercera fuente de interrupcin de los PIC's: Utilizando la intetrrupcin por cambio de estado de RB04:7 A.- Conceptos involucrados: Los conceptos son exactamente los mismos que los descritos en las parte anteriores por lo que os ruego que consultis si os es necesario: Midiendo un pulso. 1 Parte y/o 2 Parte. B.- Tcnica a Aplicar: La interrupcin por Cambio de Estado de los pines 4 a 7 del PORTB es una interrupcin muy socorrida aunque algo falta de precisicin. No dispone como en nuestros anteriores ejemplos INTEXT e INTCCP de la posibilidad de pre-programar el flanco que deseamos que dispare la Peticin de Servicio de Interrupcin (ISR). Aunque esta interrupcin, a la que a partir de aqu llamaremos INTRB, afecta en principio a los bits desde el 4 hasta el 7 del PORTB, solo lo har efectivamente a aquellos que esten configurados como de entrada, que hayamos programado con el TRIS a 1. Al dispararse la interrupcin solo sabemos que uno o mas de estos pines ha cambiado de estado. Nada mas, ni nada menos. Por ello es imprescindible al usar INTRB que vayamos recogiendo sobre una variable esttica de un byte los cambios sucesivos que se van produciendo para poder comparar en el momento de recibir la interrupcin el estado actual del PORTB con el inmediatamente anterior. As si el bit correspondiente al pin que estamos utilizando para nuestra medicin del pulso es igual en el estado actual que en el anterior no tenemos cambio en el pulso y adems no ha sido ste pin el responsable de disparar la interrupcin. Si el estado anterior era bajo y el actual es alto estamos ante el flanco de subida de nuestro pulso y adems es l quien ha producido el disparo de la interrupcin. Si por el contrario el estado anterior del pin era alto y ahora es bajo entonces acabamos de detectar el flanco de bajada de nuestro pulso, y por supuesto tambin ha

sido l el responsable de disparar la interrupcin INTRB. Con esta informacin realizaremos exactamente igual que en los ejemplos anteriores: guardamos el valor de TMR1 tras el flanco de subida y tras el de bajada, realizamos la correspondiente resta entre ambos valores y transmitimos los resultados. C.- Implementacin en C: Para configurar inicialmente nuestra interrupcin INTRB debemos hacer que nuestro pin, en este caso el 4, sea de entrada, y habilitaremos las interrupciones necesarias con: Cdigo
GeSHi (c): 1. set_tris_b(0b00010000); entrada RB7 para interrupcin RB 2. enable_interrupts(int_rb); 3. enable_interrupts(global); 4. // Habilito como

Nuestra rutina ISR para INTRB quedara como sigue: Cdigo


GeSHi (c): 1. int estado_portb_anterior=0; 2. int estado_portb_actual=0; 3. 4. ... 5. 6. #int_rb 7. void handle_rb_int(){ 8. 9. estado_portb_actual=input_b(); 10. if ((!bit_test(estado_portb_anterior,4))&&(bit_test(estado_portb_ac tual,4))) 11. { 12. hay_flanco_de_bajada=0; 13. } 14. 15. if(hay_flanco_de_bajada!=0){ // He recibido Flanco de Subida 16. t1=get_timer1(); // Guardo en t1 el valor de TMR1 al Flanco de Subida 17. } else { // He recibido Flanco de Bajada 18. t2=get_timer1(); // Guardo en t2 el valor de TMR1 al Flanco de Bajada 19. set_timer1(0); // Reinicio TMR1 20. if(flagHayDatos==0){ // Si los datos anteriores han sido procesados ... 21. flagHayDatos=1; // Indico que ya hay nuevos datos de flancos para calcular 22. } 23. }

24. } 25.

El resto de la implementacin en C de esta Tcnica es identica a la mostrada en Midiendo un pulso. 1 Parte. Tiempo en Alto con INTEXT y Midiendo un pulso. 2 Parte. Tiempo en Alto con INTCCP Bueno, y esto es todo por hoy amigos.

Midiendo un pulso. 4 Parte. El pulso completo. El Periodo y la Frecuencia A.- Conceptos involucrados: Si lo creis oportuno podis antes darle un repaso a la misma seccin de Conceptos de la parte 1 de esta Tcnica en C : Tiempo en Alto. Los coceptos son los mismos y aqu solo vamos a hacer algunas consideraciones sobre los mismos. Decamos all que el Tiempo T que transcurre ente dos flancos sucesivos de subida (o de bajada) es lo que conocemos como Periodo del Pulso (Period), o sea: lo que dura un pulso completo. Cada T segundos ( milisegundos, microsegundos) tenemos un nuevo pulso completo. Como vemos, cada pulso tiene un Tiempo en Alto (High Time) y un Tiempo en estado Bajo (Low Time). La suma de los dos es el Periodo T del pulso.

Como vemos en la imagen superior un pulso completo comienza con un flanco de subida en T1, sigue con uno de bajada en T2 y concluye en el nuevo flanco de subida T3. Entre T1 y T2 permanece el pulso en Alto, Wh, mientras que entre T2 y T3 lo hace en bajo, Wl. El tiempo transcurrido entre T1 y T3 es el Periodo del Pulso. Si este periodo lo expresamos en Segundos entonces su inverso, 1 / Periodo, es la Frecuencia del tren de pulsos. Estos dos valores son los quie vamos a calcular con nuestro PIC: el Periodo T y la frecuencia F. B.- Tcnica a Aplicar: Para medir el periodo T del pulso vamos a utilizar los dos mismos recursos que en la 1 parte de este artculo, y de los que disponen la gran mayoria de los PIC's, tanto los de la serie 18F como los 16F: El TIMER1 y la Interrupcin Externa por RB0. Para ver los detalles de estos recursos dadle tambin un vistazo a la primera parte de esta Tcnica en C 1 Parte. Tiempo en High. (Quito este trozo para no sobrepasar los lmites de un Post que nos impone el Foro )

Aunque para calcular el Periodo del pulso y/o su Frecuencia no es necesario tomar el Tiempo T2 intermedio del Flanco de Bajada, ya que solo nos son necesarios T1 y T3,

los dos flancos de Subida sucesivos, vamos a calcularlos todos ya que as tendremos tambin cunto tiempo permanece tanto en alto como en bajo y tendremos de esta forma el tanto por ciento de la primera fase sobre la segunda del pulso, o sea: El Duty Cicle. * Lo que vamos a realizar es: 1.- Configuraremos la Int_Ext por RB0 para detectar inicialmente el Flanco de Subida. 2.- Al llegarnos este Flanco de Subida guardaremos el valor en ese momento de TIMER1 y ... 3.- Cambiaremos la configuracin de la Int_Ext por RB0 para detectar el siguiente Flanco de Bajada. 4.- Cuando nos llegue el Flanco de Bajada guardaremos de nuevo el valor de TIMER1 y ... 5.- Volveremos a configurar la Int_Ext por RB0 para detectar de nuevo un Flanco de Subida. Y ... 6.- Al llegarnos este nuevo Flanco de Subida guardaremos tambin el valor en ese momento de TIMER1 y ... 7.- Con estos tres valores de TIMER1 tendremos, expresados en Tick's de TIMER1, restando el tercero del primero, el tiempo que ha durado el Pulso. Multiplicando dicho nmero de Tick's de TIMER1 por el tiempo que dura cada Tick (dependiente del cristal que usemos) tendremos el Perido T que estamos buscando. Con 1 / T obtendramos la frecuencia del Pulso. C.- Implementacin en C: Para implementar nuestro Cdigo en C vamos a centrarnos en los puntos que hemos descrito en la seccin anterior. Para configurar inicialmente el flanco de subida a detectar utilizaremos igual quen la primera parte: Cdigo
GeSHi (c): 1. ext_int_edge(0,L_TO_H); de 1er flanco de subida 2. flagToggleFlanco = 0; Flag para cambiar de flanco 3. enable_interrupts(int_ext); interrupciones necesarias 4. enable_interrupts(global); 5. // Configuro captura // inicializo el // Habilito las

Hemos aadido la variable int1 flagToggleFlanco debido a que una vez establecido el flanco a detectar no tenemos oportunidad de saber qu flanco es el que estamos esperando, as si flagToggleFlanco tiene un valor de 0 es que estamos esperando el de Subida y si por el contrario flagToggleFlanco tiene un valor de 1 entonces es el de bajada el que esperamos. Con esta configuracin inicial podemos ya escribir nuestra rutina ISR:

Cdigo
GeSHi (c): 1. #int_ext 2. void handle_ext_int(){ 3. 4. ++numFlancoQueLlega; nos llega 5. if(flagToggleFlanco==0){ Flanco de Subida 6. if(numFlancoQueLlega==1){ 7. set_timer1(0); 8. t1=get_timer1(); valor de TMR1 al primer Flanco de Subida 9. } 10. if(numFlancoQueLlega==3){ 11. t3=get_timer1(); valor de TMR1 al primer Flanco de Subida 12. if(flagHayDatos==0){ anteriores han sido procesados ... 13. flagHayDatos=1; hay nuevos datos de flancos para calcular 14. } 15. } 16. ext_int_edge(0,H_TO_L); capturar siguiente flanco de Bajada 17. flagToggleFlanco=1; siguiente flanco ser de Bajada 18. } else { Flanco de Bajada 19. t2=get_timer1(); valor de TMR1 al Flanco de Bajada 20. ext_int_edge(0,L_TO_H); capturar siguiente flanco de subida 21. flagToggleFlanco=0; siguiente flanco ser de Subida 22. } 23. FLASH; entrada mediante un LEd en E0; 24. if(numFlancoQueLlega==3){ 25. numFlancoQueLlega=0; 26. } 27. } 28.

// Cuento flanco que // He recibido // Reinicio TMR1 // Guardo en t1 el

// Guardo en t1 el // Si los datos // Indico que ya

// Configuro para // Indico que el // He recibido // Guardo en t2 el // Configuro para // Indico que el // Reproduzco la

Fijaos que en la rutina ISR anterior no realizamos ningn clculo. Aplicamos aqui el principio fundamental de que "dentro de una rutina de interrupcin haz lo debas y abandnala lo antes que puedas". Asi que si tenemos valores vlidos de T1, T2 y T3 entonces solo hacemos que flagHayDatos valga 1 y ya trataremos estos valores dentro de main(). Con numFlancoQueLlega sabremos qu flanco de Subida es T1 y cal es T3 ya que solo cuando obtengamos T3 daremos por concluida nuestra captura. Cdigo
GeSHi (c):

1.

if(flagHayDatos==1){ hay datos de flancos ... 2. if((t3>t2)&&(t2>t1)){ estoy en la misma vuelta de TMR1 3. tth = t2 - t1; de TMR1 el tiempo en Alto 4. ttl = t3 - t2; de TMR1 el tiempo en Bajo 5. tt = tth + ttl; de TMR1 el Periodo del Pulso 6. sth = uSxTick * tth; tiempo en Alto. 7. stl = uSxTick * ttl; tiempo en Bajo. 8. st = uSxTick * tt; tiempo el Periodo. 9. f = 1 / (st / 1000000); Frecuencia 10. flagHayTransmitir=1; nuevo valor para transmitir 11. } 12. flagHayDatos=0; han sido procesados los datos. 13. } 14.

// Detecto que ya // Compruebo que // Calculo en Tick's // Calculo en Tick's // Calculo en Tick's // Calculo en uS el // Calculo en uS el // Calculo en uS el // Calculo la // Indico que tengo // Indico que ya

Cuando flagHayDatos es 1 podemos proceder a calcular los Ticks transcurridos entre un flanco y otro. Fijaos que preguntamos si T3 es mayor que T2 y que ste es a su vez mayor que T1. Esto es debido a una limitacin que nos impone el no tener en cuenta vueltas completas de TIMER1 para realizar nuestros clculos: T3, T2 y T1 deben estar dentro de una misma vuelta de TIMER2, o sea que el pulso debe ser menor que 13,1072 milisegundos. Notad que cuando detectamos el primer flanco volvemos a poner TIMER1 a cero para recomenzar siempre cerca del inicio de la cuenta de TIMER1 y evitar asi su desborde. Para computar tiempos de pulso mayores 13,1072 milisegundos solo habra que computar tambien cuantas veces se desborada completo TIMER1 y realizar los clculos aritmticos correspondientes. Al igual que hacamos en la Rutina ISR en que no calculbamos sino que solo indicbamos que podamos calcular, aqu una vez realizados los calculos de Ticks y su equivalencia en segundos no los transmitimos, slo indicamos que hay datos para transmitir mediante poner a 1 flagHayTransmitir. Para transmitir el ltimo valor computado vamos a implementar una recepcin va serie RS232 que al recibir el comando 'T' nos enva el ltimo valor correcto registrado: Cdigo
GeSHi (c): 1. if(Command!=0x00){ comando va Serie ... // Si he recibido un

2.

if(Command=='T'){ // Si el comando es 'T' (Transmite) ... 3. if(flagHayTransmitir==1){ // Si hay algo pendiente de transmitir ... 4. printf("Ticks ....... H %4Lu + L %4Lu = %4Lu\r\n",tth,ttl,tt); 5. printf("uSegundos ... H %3.1f + L %3.1f = %3.1f F = %4.2f Hz\r\n\n",sth,stl,st,f); 6. flagHayTransmitir=0; // Indico que ya he transmitido lo pendiente. 7. } 8. } 9. Command=0x00; // Indico que ya he procesado el comando. 10. } 11.

Con todo esto el programa completo para nuestro Detector de Periodo y Frecuencia de un Pulso queda as: Cdigo
GeSHi (c): 1. //////////////////////////////////////////////////////////////// //////////////////// 2. // 3. // Midiendo_un_pulso_2_Complete.c 4. // 5. // SERIE: "Tcnicas en C" para el Foro TODOPIC 6. // 7. // (c) 10.2006 by RedPic 8. // 9. // Propsito: Medir los tiempos en alto y bajo que permanece un pulso : Periodo y Frecuencia 10. // 11. // Condiciones de Test: Inyeccin por RB0 de una seal de 2 Khz (0.5 ms de periodo) 12. // 13. // Tcnica Empleada: Detectar mediante la Interrupcin Externa por RB0 14. // un flanco de subida de un pulso, guardar el estado 15. // de TMR1, detectar a continuacin el siguiente 16. // flanco de bajada, guardar el nuevo estado de TMR1, 17. // detectar a continuacin un nuevo flanco de subida, 18. // guardar nuevamente el estado de TMR1 y realizar la 19. // correspondiente substraccin de ellos para obtener 20. // el tiempo que permanece en alto y bajo y transmitir 21. // los resultados mediante el puerto RS232 a peticin. 22. // 23. /////////////////////////////////////////////////////////////// /////////////////////

24. 25. 26. #include <18f4550.h> 27. #fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NO DEBUG,NOWRT,NOVREGEN 28. #use delay(clock=20000000) 29. 30. #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) 31. 32. 33. /////////////////////////////////////////////////////////////// ///////////////////// 34. // 35. // Defines y Constantes 36. // 37. /////////////////////////////////////////////////////////////// ///////////////////// 38. 39. #define LED PIN_E0 // Defino el Pin del Led 40. #define FLASH Output_Toggle(LED) // Defino la funcion Flash de monitor 41. 42. float const uSxTick = 0.2; // Microsegundos por Tick de TMR1 a 20 Mhz 43. 44. 45. /////////////////////////////////////////////////////////////// ///////////////////// 46. // 47. // Variables en RAM 48. // 49. /////////////////////////////////////////////////////////////// ///////////////////// 50. 51. char cRec=0x00; // ltimo caracter recibido via serie 52. char Command=0x00; // Comando a procesar 53. int8 numFlancoQueLlega=0; // Nmero de Flanco que llega 54. int1 flagToggleFlanco=0; // Flag para cambiar de flanco 55. int16 t1=0x00,t2=0x00,t3=0x00; // Variables para guardar estados de ... 56. int16 tth=0x00,ttl=0x00,tt=0x00; // Timers y pulsos. 57. float sth=0.0,stl=0.0,st=0.0,f=0.00; // Para hacer las restas oportunas en uS 58. int1 flagHayDatos=0; // Flag para indicar que ya hay datos de .. 59. // dos flancos (de subida y bajada) 60. int1 flagHayTransmitir=0; // Flag para indicar que hay datos para ... 61. // Transmitir al PC. 62. 63. /////////////////////////////////////////////////////////////// ///////////////////// 64. //

65. // Interrupcin por Recepcin Serie RS232 66. // 67. /////////////////////////////////////////////////////////////// ///////////////////// 68. 69. 70. #int_rda 71. void handle_rda_int(){ 72. 73. if(kbhit()){ // Si hay algo pdte de recibir ... 74. cRec=getc(); // lo recibo sobre cRec ... 75. if(cRec!=0x00){ // Si es distinto de \0 ... 76. Command=ToUpper(cRec); // cargo cRec sobre Command para procesarlo 77. } // pasndolo a Maysculas para no confundir. 78. } 79. } 80. 81. /////////////////////////////////////////////////////////////// ///////////////////// 82. // 83. // Interrupcin por Externa por Cambio de Flanco en RB0 84. // 85. /////////////////////////////////////////////////////////////// ///////////////////// 86. 87. 88. #int_ext 89. void handle_ext_int(){ 90. 91. ++numFlancoQueLlega; // Cuento flanco que nos llega 92. 93. if(flagToggleFlanco==0){ // He recibido Flanco de Subida 94. 95. if(numFlancoQueLlega==1){ 96. set_timer1(0); // Reinicio TMR1 97. t1=get_timer1(); // Guardo en t1 el valor de TMR1 al primer Flanco de Subida 98. } 99. if(numFlancoQueLlega==3){ 100. t3=get_timer1(); // Guardo en t1 el valor de TMR1 al primer Flanco de Subida 101. if(flagHayDatos==0){ // Si los datos anteriores han sido procesados ... 102. flagHayDatos=1; // Indico que ya hay nuevos datos de flancos para calcular 103. } 104. } 105. ext_int_edge(0,H_TO_L); // Configuro para capturar siguiente flanco de Bajada 106. flagToggleFlanco=1; // Indico que el siguiente flanco ser de Bajada 107. 108. } else { // He recibido Flanco de Bajada

109. 110. t2=get_timer1(); // valor de TMR1 al Flanco de Bajada 111. ext_int_edge(0,L_TO_H); // capturar siguiente flanco de subida 112. flagToggleFlanco=0; // siguiente flanco ser de Subida 113. } 114. 115. FLASH; // entrada mediante un LEd en E0; 116. 117. if(numFlancoQueLlega==3){ 118. numFlancoQueLlega=0; 119. } 120. 121. } 122. void main() { 123. 124. 125. ////////////////////////////////////////// GENERALES 126. 127. delay_ms(333); // todo se estabilice e ... 128. disable_interrupts(global); // Micro y ... 129. disable_interrupts(int_timer1); // todo lo no necesario ... 130. disable_interrupts(int_rda); 131. disable_interrupts(int_ext); 132. disable_interrupts(int_ext1); 133. disable_interrupts(int_ext2); 134. setup_adc_ports(NO_ANALOGS); 135. setup_adc(ADC_OFF); 136. setup_spi(FALSE); 137. setup_psp(PSP_DISABLED); 138. setup_counters(RTCC_INTERNAL,RTCC_DIV_2); 139. setup_timer_0(RTCC_OFF); 140. setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); 141. setup_timer_2(T2_DISABLED,0,1); 142. setup_timer_3(T3_DISABLED); 143. setup_comparator(NC_NC_NC_NC); 144. setup_vref(FALSE); 145. port_b_pullups(FALSE); 146. delay_ms(333); 147. 148. /////////////////////////////////////////// PERTINENTES A LA APLICACION 149. 150. set_tris_c(0b10000000); // entrada RC7 para canal RS232 151. 152. ext_int_edge(0,L_TO_H); // captura de 1er flanco de subida 153. flagToggleFlanco = 0; // Flag para cambiar de flanco 154. 155. enable_interrupts(int_rda); // interrupciones necesarias 156. enable_interrupts(int_ext);

Guardo en t2 el Configuro para Indico que el

Reproduzco la

INICIALIZACIONES Espero a que Inicializo el deshabilitando

INICIALIZACIONES Habilito como Configuro inicializo el Habilito las

157. enable_interrupts(global); 158. 159. printf("\r\nMidiendo un pulso : Periodo\r\n"); 160. printf("By Redpic para Foro TODOPIC\r\n\n"); 161. 162. do { 163. 164. if(flagHayDatos==1){ // Detecto que ya hay datos de flancos ... 165. if((t3>t2)&&(t2>t1)){ // Compruebo que estoy en la misma vuelta de TMR1 166. tth = t2 - t1; // Calculo en Tick's de TMR1 el tiempo en Alto 167. ttl = t3 - t2; // Calculo en Tick's de TMR1 el tiempo en Bajo 168. tt = tth + ttl; // Calculo en Tick's de TMR1 el Periodo del Pulso 169. sth = uSxTick * tth; // Calculo en uS el tiempo. 170. stl = uSxTick * ttl; // Calculo en uS el tiempo. 171. st = uSxTick * tt; // Calculo en uS el tiempo. 172. f = 1 / (st / 1000000); // Calculo la Frecuencia 173. 174. flagHayTransmitir=1; // Indico que tengo nuevo valor para transmitir 175. } 176. flagHayDatos=0; // Indico que ya han sido procesados los datos. 177. } 178. if(Command!=0x00){ // Si he recibido un comando va Serie ... 179. if(Command=='T'){ // Si el comando es 'T' (Transmite) ... 180. if(flagHayTransmitir==1){ // Si hay algo pendiente de transmitir ... 181. 182. printf("Ticks ....... H %4Lu + L %4Lu = %4Lu\r\n",tth,ttl,tt); 183. printf("uSegundos ... H %3.1f + L %3.1f = %3.1f F = %4.2f Hz\r\n\n",sth,stl,st,f); 184. 185. flagHayTransmitir=0; // Indico que ya he transmitido lo pendiente. 186. } 187. } 188. Command=0x00; // Indico que ya he procesado el comando. 189. } 190. 191. 192. } while (TRUE); 193. } 194.

D.- Ejemplo funcionando:

También podría gustarte