Problemas con TIMER0 y PIC 16F877A

#1
Buenas, el problema es el siguiente, estoy diseñando un tacomentro para auto con un PIC y un LCD.
La primer versión contaba las variaciones de entradas en la pata A1 por cada siclo completo del programa (incluía un delay_ms(1) al final del bucle principal), al tener 200ms de registros procesaba la información.
Esto no me permitía tener una buena resolución, procesando cada 200ms tengo una resolución de 150rpm.

Opte por contar 4 pulsos y en base al tiempo transcurrido medir las rpm.
Esta cuenta la hice agregando un dalay_us(100) al final del bucle principal.
Para hacerlo mas profesional pase a usar la interrupción TIMER0 pero el problema es que esta interrupción esta corriendo mas lento de lo que debería y reporta peor las RPM que el precario sistema de poner un delay al final del bucle principal.

El codigo es el siguiente:

Código:
#include <16f877A.h>                         /* PIC16F877A */
#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP,BROWNOUT /* Fusibles (lo mas comun) */                                            
#use delay (clock=20000000)          /* Ajuste retardos 20M*/
//#use delay (clock=4000000)          /* Ajuste retardos 4M*/
#use fast_io(a)                              /* Opcional */

#define LED_SHIFT  PIN_C1
#define RPM_SIG PIN_A0 //IN

#include <lcd.c>

//VARIABLES!
int32 tmp1;//USO TEMPORAL.-
int32 tmp2,tmp3,tmp4;//USO TEMPORAL.-
long rpmant,rpm,rpmcount;//RESULTADO DE RPM Y CUENTA PARCIA.-
short int rpmhi,velhi;//GUARDA ESTADO DE SENSORES.-

int32 rpmtime1,rpmtime2;

void sensores2()
{
	if((input(RPM_SIG)) && !rpmhi)
	{
		rpmhi=1;
		rpmcount++;
		if(rpmcount==1)
		{
			rpmtime1=tmp3;
		}
		else if(rpmcount==2)
		{
		/*
			DEBERIA HACER 20.000 SICLOS CADA 1 SEGUNDO
			EN REALIDAD HACE 15.533 POR SEGUNDO
		*/
			rpmcount=0;
			rpmtime2=tmp3;
			tmp1=rpmtime2-rpmtime1;
			//rpm=1200000/2/tmp1; //VALOR IDEAL (20.000 * 60) /2 /tmp1
			rpm=932000/2/tmp1; //VALOR FORZADO (15533 * 60) /2 /tmp1
			tmp3=0;
		}
	}
	if((!input(RPM_SIG)) && rpmhi)
	{
		rpmhi=0;
	}
}


#int_TIMER0
Void TIMER0_isr(void)
{

	tmp3++; //LO USO PARA VER LA VARIACION EN TIEMPO DENTRO DE sensores(); 
	tmp2++; //LO USO PARA PRENDER Y APAGAR UN LED CADA 1 SEGUNDO.

	if(tmp2==10000)
	{
		output_high(LED_SHIFT);
	}
	if(tmp2==20000)
	{
		tmp2=0;
		output_low(LED_SHIFT);
	}

	sensores2(); //RECIBO DATA DE RPM Y PROCESO.
	set_timer0(0);

}

//PROGRAMA PRINCIPAL !!!
void main(void)
{
	lcd_init();

	setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
	enable_interrupts(GLOBAL | INT_TIMER0); 
	set_timer0 (0);

	rpmcount=0;
	rpm=0;

	while(TRUE)
	{

		printf(lcd_putc,"\fTMP1: %Lu", rpmtime2);
		printf(lcd_putc,"\nTMP2: %Lu", rpm);

		delay_ms(1);
	}
}
Espero se haya entendido y puedan ayudarme.
Saludos,
Federico.
 
#2
Hola, en tu caso para un programa siempre y que se necesita un buena precisión para medir tiempos, haría el programa en ASM. Un saludo
 
#3
Pero estoy haciendo algo mal? es bastante simple como para que falle me parece.
La verdad asm no se nada de nada, a C me estoy adaptando porque manejo php y perl.
 
#6
La verdad es que ya no se que pensar, les muestro el ultimo codigo de pruebas.
Defino TIMER0 y TIMER1 (son lo que pienso usar en mi programa).
Cambiando el Prescaler del TIMER0 me varia considerablemente el resultado:

CODIGO:
Código:
#include <16f877A.h>                         /* PIC16F877A */
#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP,BROWNOUT /* Fusibles (lo mas comun) */                                            
#use delay (clock=20000000)          /* Ajuste retardos 20M*/
//#use delay (clock=4000000)          /* Ajuste retardos 4M*/
#use fast_io(a)                              /* Opcional */

#include <lcd.c>
int32 tmp1;//USO TEMPORAL.-

#int_TIMER0
void TIMER0_isr(void)
{
	//tmp1 ++;
	//delay_us(1);
}

//PROGRAMA PRINCIPAL !!!
//PROGRAMA PRINCIPAL !!!
void main(void)
{
//CON ESTO EL LCD IMPRIME 6318:
	setup_timer_0(T0_INTERNAL | RTCC_DIV_1);
//CON ESTO EL LCD IMPRIME 5122:
//	setup_timer_0(T0_INTERNAL | RTCC_DIV_8);

	enable_interrupts(GLOBAL | INT_TIMER0);
	set_timer0(0);


	setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
	set_timer1(0);

	lcd_init();
	while(TRUE)
	{
		tmp1=get_timer1();
		printf(lcd_putc,"\f%Lu", tmp1);
		set_timer1(0);
		delay_us(1000);
	}
}
La verdad no se que hacer, porque cualquier cambio varia la precisión de los TIMERS.
Sera un problema de la simulación?
 
#7
Podes hacer asi, no programo en C asi que te lo explico en palabras. (lo calcule con un cristal de 20Mhz)

Usas 2 interrupciones, una por timer y otra por RB0

La interrupcion TIMER0 la configuras para que salte cada 10us e incremente la variable "tiempo"
Configuras el preescaler en 2 y el TMR0 en 230
entonces


A 20 Mhz son 5000000 int/seg
5000000/2=2500000 inst/seg (divido por 2 con el preescaler)
2500000/25= 10000 inst/seg (El TMR0 esta en 230 entonces cuenta 25 y desborda)
1seg/10000 = 0.0001 = 10us (Salta la interrupcion TMR0 casa 10us)


Cuando salta el timer pone que incremente en 1 la variable tiempo "inc tiempo" o "tiempo = tiempo +1"
Y Cuando salta la interrupcion RB0 pone que haga esta cuenta y te da las rpm "RPM=6000000/tiempo"


Bueno espero haber echo bien los calculos
 
Última edición:

Temas similares

Arriba