| |
TÉCNICAS EN
C
|
|
| |
Tareas
Comunes Realizadas
en
C
|
|
| |
Esta serie de contenidos
titulada Técnicas en C pretende
mostrar con ejemplos comentados la forma de implementar ciertos
procesos muy usuales durante el uso de los Microcontroladores PIC de
las series 16F y 18F. |
|
|
|
Midiendo
un pulso. 1ª Parte. Tiempo en Alto mediante 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
tensión 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
| B.-
Técnica a Aplicar: |
| |
|
|
|
|
|
|
-
Cuando el TIMER1
alcanza el valor 0xFFFF continúa de nuevo por 0x0000, y así hasta
el infinito y mas allá. (Cuando pasa de 0xFFFF a 0x0000 genera una
Interrupción por Desbordamiento de Timer pero que no vamos a utilizar en
este ejemplo, así que no vamos a entrar en cómo 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 Interrupción
Externa por RB0, conocida como INTEXT consiste en una
petición de interrupción 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 código para
ejecutarlo cuando ese tipo de flanco, subida o bajada, es detectado. Es
lo que conocemos como Petición de Servicio por Interrupción (Interrupt
Service Request ó ISR)
|
|
|
|
Lo que vamos a realizar
exactamente es:
|
|
|
|
|
|
-
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 número 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.-
Implementación en C: |
| |
|
|
|
|
| |
|
|
| |
ext_int_edge(0,L_TO_H); // Configuro captura de 1er flanco de subida
flagToggleFlanco = 0; // inicializo el Flag para cambiar de flanco
enable_interrupts(int_ext); // Habilito las interrupciones necesarias
enable_interrupts(global);
|
|
| |
|
|
-
Hemos añadido 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.
|
|
|
| |
|
|
| |
#int_ext
void handle_ext_int(){
if(flagToggleFlanco==0){ // He recibido Flanco de Subida
t1=get_timer1(); //
Guardo en t1 el valor de TMR1 al Flanco de Subida
ext_int_edge(0,H_TO_L); // Configuro para capturar siguiente
flanco de Bajada
flagToggleFlanco=1; // Indico que el
siguiente flanco será de Bajada
} else {
// He recibido Flanco de Bajada
t2=get_timer1(); //
Guardo en t2 el valor de TMR1 al Flanco de Bajada
ext_int_edge(0,L_TO_H); // Configuro para capturar siguiente
flanco de subida
flagToggleFlanco=0; // Indico que el
siguiente flanco será de Subida
set_timer1(0);
// Reinicio TMR1
if(flagHayDatos==0){ // Si los datos
anteriores han sido procesados ...
flagHayDatos=1;
// Indico que ya hay nuevos datos de flancos para calcular
}
}
} |
|
| |
|
|
| |
|
|
| |
if(flagHayDatos==1){ // Detecto que ya hay
datos de flancos ...
if(t2 > t1){
// Compruebo que estoy en la misma vuelta de TMR1
tt = t2 - t1;
// Calculo en Tick's de TMR1 el tiempo entre flancos
st = uSxTick * tt; // Calculo en uS
el tiempo.
flagHayTransmitir=1; // Indico que tengo nuevo
valor para transmitir
}
flagHayDatos=0; //
Indico que ya han sido procesados los datos.
} |
|
| |
|
|
- 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 limitación que nos impone el no tener en
cuenta vueltas completas de TIMER1 para realizar nuestros cálculos: 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 así su desborde.
|
- Para computar tiempos de pulso mayores
13,1072 milisegundos solo habría que computar también cuantas veces se
desborda completo TIMER1 y realizar los cálculos aritméticos
correspondientes.
|
- Al igual que hacíamos en la Rutina ISR
en que no calculábamos sino que solo indicábamos que podíamos calcular,
aquí una vez realizados los calculos de Tick´s y su equivalencia en
segundos no los transmitimos, sólo indicamos que hay datos para
transmitir mediante poner a 1 flagHayTransmitir.
|
- Para transmitir el último valor
computado vamos a implementar una recepción vía serie RS232 que al
recibir el comando 'T' nos envía el último valor correcto
registrado:
|
| |
|
|
| |
if(Command!=0x00){
// Si he recibido un comando vía 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.
} |
|
| |
|
|
|
|
- Con todo esto el programa completo para
nuestro Detector de Tiempo en Alto de un Pulso
queda así:
|
|
|
| |
|
|
| |
////////////////////////////////////////////////////////////////////////////////////
//
// Midiendo_un_pulso_1_INTEXT.c
//
// SERIE: "Técnicas en C" para el Foro TODOPIC
//
// (c) 10.2006 by RedPic
//
// Propósito: Medir el tiempo que permanece un pulso en alto
//
// Condiciones de Test: Inyección por RB0 de una señal de 2 Khz (0.5 ms de
periodo)
//
// Técnica Empleada: Detectar mediante la Interrupción Externa por RB0
//
un flanco de subida de un pulso, guardar el estado
//
de TMR1, detectar a continuación el siguiente
//
flanco de bajada, guardar el nuevo estado de TMR1,
//
realizar las substracción de ambos para obtener el
//
tiempo que permanece en alto y transmitir mediante
//
el puerto RS232 a petición.
//
////////////////////////////////////////////////////////////////////////////////////
#include <18f4550.h>
#fuses
HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
////////////////////////////////////////////////////////////////////////////////////
//
// Defines y Constantes
//
////////////////////////////////////////////////////////////////////////////////////
#define LED PIN_E0
// Defino el Pin del Led
#define FLASH Output_Toggle(LED) // Defino la función Flash de monitor
float const uSxTick = 0.2; //
Microsegundos por Tick de TMR1 a 20 Mhz
////////////////////////////////////////////////////////////////////////////////////
//
// Variables en RAM
//
////////////////////////////////////////////////////////////////////////////////////
char cRec=0x00;
// Último carácter recibido vía serie
char Command=0x00;
// Comando a procesar
int1 flagToggleFlanco=0; // Flag para
cambiar de flanco
int16 t1=0x00,t2=0x00,tt=0x00; // Variables para guardar estados de ...
float st=0.0;
// TMR1 en cada flanco y hacer la resta
int1 flagHayDatos=0;
// Flag para indicar que ya hay datos de ..
// dos flancos (de subida y bajada)
int1 flagHayTransmitir=0; // Flag para indicar
que hay datos para ...
// Transmitir al PC.
////////////////////////////////////////////////////////////////////////////////////
//
// Interrupción por Recepción Serie RS232
//
////////////////////////////////////////////////////////////////////////////////////
#int_rda
void handle_rda_int(){
if(kbhit()){
// Si hay algo pdte de recibir ...
cRec=getc();
// lo recibo sobre cRec ...
if(cRec!=0x00){
// Si es distinto de \0 ...
Command=ToUpper(cRec); // cargo cRec
sobre Command para procesarlo
}
// pasándolo a Mayúsculas para no confundir.
}
}
////////////////////////////////////////////////////////////////////////////////////
//
// Interrupción por Externa por Cambio de Flanco en RB0
//
////////////////////////////////////////////////////////////////////////////////////
#int_ext
void handle_ext_int(){
if(flagToggleFlanco==0){ // He recibido Flanco de
Subida
t1=get_timer1();
// Guardo en t1 el valor de TMR1 al Flanco de Subida
ext_int_edge(0,H_TO_L); // Configuro para
capturar siguiente flanco de Bajada
flagToggleFlanco=1;
// Indico que el siguiente flanco será de Bajada
} else {
// He recibido Flanco de Bajada
t2=get_timer1();
// Guardo en t2 el valor de TMR1 al Flanco de Bajada
ext_int_edge(0,L_TO_H); // Configuro para
capturar siguiente flanco de subida
flagToggleFlanco=0;
// Indico que el siguiente flanco será de Subida
set_timer1(0);
// Reinicio TMR1
if(flagHayDatos==0){ //
Si los datos anteriores han sido procesados ...
flagHayDatos=1;
// Indico que ya hay nuevos datos de flancos para calcular
}
}
FLASH;
// Reproduzco la entrada mediante un LEd en E0;
}
void main() {
////////////////////////////////////////// INICIALIZACIONES GENERALES
delay_ms(333);
// Espero a que todo se estabilice e ...
disable_interrupts(global); // Inicializo el
Micro y ...
disable_interrupts(int_timer1); // deshabilitando todo lo no necesario
...
disable_interrupts(int_rda);
disable_interrupts(int_ext);
disable_interrupts(int_ext1);
disable_interrupts(int_ext2);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_psp(PSP_DISABLED);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(FALSE);
delay_ms(333);
/////////////////////////////////////////// INICIALIZACIÓN PERTINENTE A
LA APLICACIÓN
set_tris_c(0b10000000);
// Habilito como entrada RC7 para canal RS232
ext_int_edge(0,L_TO_H);
// Configuro captura de 1er flanco de subida
flagToggleFlanco = 0;
// inicializo el Flag para cambiar de flanco
enable_interrupts(int_rda); // Habilito las
interrupciones necesarias
enable_interrupts(int_ext);
enable_interrupts(global);
printf("\r\nMidiendo un pulso : Width High\r\n");
printf("By Redpic para Foro TODOPIC\r\n\n");
do {
if(flagHayDatos==1){
// Detecto que ya hay datos de flancos ...
if(t2 > t1){
// Compruebo que estoy en la misma vuelta de TMR1
tt = t2 - t1;
// Calculo en Tick's de TMR1 el tiempo entre flancos
st = uSxTick * tt;
// Calculo en uS el tiempo.
flagHayTransmitir=1;
// Indico que tengo nuevo valor para transmitir
}
flagHayDatos=0;
// Indico que ya han sido procesados los datos.
}
if(Command!=0x00){
// Si he recibido un comando vía 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.
}
} while (TRUE);
} |
|
| |
|
|
Esta página se modificó el
27/12/2008
|