TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: ADY12 en 17 de Abril de 2015, 05:11:28
-
Hola, estaria interesado en realizar un velocimetro con el TIMER0, pero no tengo mucha idea de como hacerlo. Si me pudierais ayudar. Utilizo el pic18F4550
-
de que quieres medir la velocidad y de que hardware dispones?
-
tengo el pic18f4550, un cristal de 20MHz, ccs compiler, y lo quiero para medir la velocidad de una motocicleta 49cc mediante un sensor.
-
el Sensor ya lo tienes? que tipo es?
puede ser un paso por 0 o un encoder, en caso de que sea encoder que es incremental absoluto?
lo mejor para esta aplicación es un paso por cero optico o de efecto hall, necesitas saber el diametro de la rueda.
de esta manera cuando pase por 0 empiezas a contar y cuando vuelva a pasar por 0 paras de contar, hay tienes el tiempo que ha tardado en dar una vuelta completa, si saber el radio de la rueda ya puedes calcular la velocidad angular y de hay la velocidad lineal, partiendo de que no hay deslizamiento.
-
tengo un sensor efecto hall, el diametro sera uno cualquiera porque sera para demostrar que funciona en un proyecto.
-
Quedan 2 opciones..
Una es contar el tiempo entre vueltas (cada ves que el iman pasa sobre el sensor), esto te va a dar una idea de a que velocidad va la moto. tendrias que sacar un promedio de varias lecturas antes de mostrarlas
La otra es contar la cantidad de vuelta que hay en 1 segundo, esto tambien te daria la velocidad, para mi esta seria la indicada. Con un timer y un modulo CCP creo que es totalmente posible, solo tendrias que recoger el dato y mostrarlo.
-
Hola, no comprendo porque pretendés emplear el timer0, si no es un requisito de tu proyecto, dado que es para algo tuyo, como has indicado.
La forma fácil de hacerlo sin liarse es emplear el módulo CCP, con su utilidad CAPTURE (también tiene compare y pwm), aquí se emplea el TIMER1 como base de tiempo/contador, programable, y con el módulo ccp se capturan sus cuentas, todo por hardware, según hallas configurado la circuiteria, pues con unos simples cálculos, obtenés el tiempo en el que la rueda dio una revolución ycon ello calculas la velocidad.
-
Te escribo esto:
lo mejor para esta aplicación es un paso por cero optico o de efecto hall, necesitas saber el diametro de la rueda.
de esta manera cuando pase por 0 empiezas a contar y cuando vuelva a pasar por 0 paras de contar, hay tienes el tiempo que ha tardado en dar una vuelta completa, si saber el radio de la rueda ya puedes calcular la velocidad angular y de hay la velocidad lineal, partiendo de que no hay deslizamiento.
Y me contestas esto:
tengo un sensor efecto hall, el diametro sera uno cualquiera porque sera para demostrar que funciona en un proyecto
No se porque pero me da la impresión de que lo que quieres es que te hagan el proyecto :D :D, debes de empezar a hacerlo por ti mismo, y en las dudas que te vayan surgiendo ves preguntando que seguro te las resolverán, pero debes ser mas claro y explicar bien tu problema, para que te puedan ayudar.
-
Hola a todos, seguramente no me explique bien lo que quería hacer y tampoco pretendía que me hicieseis el programa, lo siento si pareció eso.
Tengo conectado en el pin 6 (RA4/T0CK1) del pic18F4550 el sensor inductivo. Lo que pretendo realizar es lo siguiente. Es mediante una rueda de pequeñas dimensiones de unos 8 cm de diámetro, yo mismo le daré impulso con la mano, para que coja velocidad y poder simular lo que haría en la rueda de una moto a pequeña escala. Estoy probando de hacer el programa, mediante que el TIMER0 cuente pulsos (aplicando la formula de velocidad), pero no consigo hacerlo bien y me estoy liando, si pudierais aconsejarme. Gracias.
#include <18F4550.h>
#fuses NOWDT,XT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include "C:\Users\xx\Desktop\VELOCIDAD\flex_lcd420.c"
int16 counter;
float Velocidad=0;
#int_TIMER0
void TIMER0_isr(){
counter=get_timer0(); //lee contador TMR0
Velocidad= Counter * 25.13 * 0.036;
printf(lcd_putc,"%61u vel", Velocidad);
lcd_gotoxy(1,1);
counter=0;
Velocidad=0;
set_timer0(0); //Reinicia cuenta
}
void main(){
lcd_init();
setup_timer_0(rtcc_ext_l_to_h|RTCC_DIV_2); //Configuración TMR0
set_timer0(0); //Borrado contador
enable_interrupts(global);
}
-
Bueno ese programa no puede funcionar lo primero que veo que falta es el bucle while principal, si no lo pones tu programa terminara y ya no hará nada, esto no puede faltar nunca. Colocarlo y veras como podrás realizar mas pruebas pero así no podrás hacer nada.
Otra cosa que veo es que no configuras los pines en ningún lado, no recuerdo bien el ccs, pero debes configurar tu pin como entrada.
Te recomendaría olvides primero formulas y todo, pon el while configura el primer y revisa si entra en la interrupción, cuando eso funciones entonces sumas la pantalla y las cuentas.
Un saludo.
-
Hay 2 errores mas podria agregar,
El primero es hacer calculos/escribir en el LCD en una interrupcion. Aunque depende de que tan rapido lo haga y si no necesita de otra interrupcion.
El segundo es que tiene un mal concepto de que es una interrupcion en el Timer. al ver que en la interrupcion pregunta por que valor tiene el timer. Si ocurre la interrupcion por el Timer es por que paso de 0xFF a 0x00 ( si es 8 bits ) por lo tanto siempre leerias 0x00.
Empecemos como vas a medir la velocidad de la rueda, si pienso en timers se me ocurren 2 formas.
- Medir entre 2 pulsos del sensor para saber cuando tardo en dar una vuelta.
- Medir en un tiempo conocido cuantas vueltas pulsos dio el sensor ( Si lo hago cada un segundo, lo unico que tendria que hacer al numero que me de es multiplicarlo por 60 y ya tendria las RPM para pasarlo a velocidad deberia saber el radio de la rueda )
Vamos por parte dijo Jack.
El primer caso:
Significa que deberia tener 2 interrupciones, una de una entrada externa (sea por RB0 o por CCP) y otra del timer. Lo ideal un timer de 16bits
Al producirse la primer interrupcion por flanco ascendente pongo a 0 el timer y recien ahi habilito la interrupcion del timer.
La segunda interrupcion por flanco ascendente, tomo el valor del timer y luego lo pongo a 0, salgo aviso con un flag que ya esta listo el dato y que se procese en el main.
Proxima interrupcion por flanco ascendente, nuevamente tomo el valor y lo pongo a 0 nuevamente, aviso con un flag que entro y que se procese en el main.
Posibles problemas:
Que puede pasar que se tarde mucho en cruzar el sensor (se detenga la rueda o vaya muy lento), entonces deberias tener un "seguro" o la capacidad de contar mucho mas.
Con la interrupcion del timer vas a saber si el timer rebalso por completo y si pones un contador vas a saber cuantas veces sucedio, esto si o si tiene que estar.
Para este caso no conviene usar el Timer 0, conviene usar el Timer 1 o 3, que se usa con el modulo CCP. Esto hace que apenas se detecte un flanco ascendente/descendente se guarde automaticamente en otro registro el valor del timer y Se genera una interrupcion, de todas formas deberias de tener en cuenta lo anterior con respecto a los timers. Si es de 16 bits como cuando lo usas con el CCP incluso podes no ponerlo a 0 al timer y luego hacer la diferencia, valor nuevo - valor viejo = tiempo que paso
Segundo caso:
En este caso tambien seria ideal un timer de 16 bits o tal ves con 8 alcance, asi intentar llegar al segundo o tal ves valores como 0.2s,0.1s,etc
Aqui solamente configuras tu Temporizador a un tiempo predeterminado como dije antes y habilitas el modulo CCP para que cuente los flancos ascendentes ( si son muchos podes incluso dividirlo con su preescaler )
Cuando ocurre la interrupcion del Timer, guardas el valor que quedo del CCP, lo pones a 0 y avisas mediante un flag al main que ya esta listo para procesarse la informacion. Tambien recargas tu timer.
En tu main Si fuera de 0.1s el timer deberias multiplicar por 600 el valor almacenado en el CCP para saber cuantas RPM tenes.
-
He seguido vuestros consejos, pero ahora me quedado estancado y nose si voy bien o voy mal, y por donde tirar. He creado la interrupcion, pero cuando me dices que guarde el valor del CCP y luego lo ponga a 0, te refieres dentro del main o en la propia interrupcion?
#include <18F4550.h>
#fuses NOWDT,XT,MCLR
#use delay(clock=20000000)
#include "C:\Users\xx\Desktop\VELOCIDAD\flex_lcd420.c"
int1 pulso=0;
float RPM=0;
int16 TFS=0;
#int_CCP1
void CCP1_irs() //Interrupcion
{
setup_ccp1(CCP_CAPTURE_RE); //Configuracion modo Captura
TFS=CCP_1;
if (pulso==0){ //FIn de pulso
pulso=1;
} //Pulso a medir
}
void main(){
lcd_init();
setup_timer_1(T1_INTERNAL); //Configuracion TMR1
setup_ccp1(CCP_CAPTURE_RE); //Configuracion modo Captura en flanco subida
enable_interrupts(int_ccp1);
enable_interrupts(global);
do {
if (pulso==1){ //¿Pulso nuevo?
RPM=TFS*60;
printf(lcd_putc," %61u RPM", RPM);
pulso=0; //Espera pulso
}
}
while(TRUE);
}
-
Perdon por la tardanza.
Cuando me referi que lo pongas a 0 es al timer.
El CCP ante un evento copia el valor del timer a su registro. Si no lo borras deberias tener los 2 ultimos valores y realizar la resta de esos 2 valores, complicandose en el manejo cuando el timer hace un overflow.
EN fin tu rutina de interrupcion seria:
CCP:
Poner a 0 timer 1
Copiar valor del CPP a una variable
Habilitar una flag, poner a 1 una flag, ni siquiera preguntar si estaba en 0.
Timer1:
flag = 0
contador++;
Tu main:
Aca configuracion de todos los modulos
While (1)
{
se activo la flag ?
SI ->
aca se puede hacer un promediado de varias entradas antes de hacer todos los calculos y mostrar
valor del timer * tiempo de cada pulso de clock (Tosc si no hay preescaler) + (tiempo de overflow del timer * contador) = Tiempo transcurrido de una vuelta (esto es algo que te va a quedar a la e-6 )
3600 / tiempo transcurrido en una vuelta = RPM
flag = 0
}
Es la primera opcion y bastante complicada la segundo caso se hace mas facil
Interrup:
Timer:
Cargo valor de ser necesario
ContadorT++;
flag=1
CCP:
ContadorPulsos++;
Main:
Conf de todas las cosas
Timer a 0.2s el overflow (Voy a suponer que llega a 0.2s)
while(1)
{
Flag = 1 ?
Si ->
Contador de pulsos * 5 * 60 = RPM
Flag = 0
}
El 5 es para que sea de 0.2s a 1s y luego los 60 para que sean 60s , podrias tambien tomar 5 muestras, sumarlas y luego multiplicarlas por 60. O tambien hacer que a la 5ta entrada de la interrupcion del timer ( sigo suponiendo que lo haces a 0.2s) habilite el flag
-
Perdon por el retraso, he estado fuera unos dias y no he podido contestar. He hecho lo que me comentaste, tengo conectado el sensor en el pin 18 (C2) para el modo captura. Tengo una duda en relacion donde introducir el printf si dentro del if o fuera. Porque lo he probado tanto dentro del if como fuera y me aparece dígitos que no tienen sentido en el LCD. Otra cosa que he hecho a sido el decir que el C2 sea entrada, pero en el puerto C tambien uso el LCD que he dicho que continue siendo salida. En la carga del timer tengo dudas si he decido la correcta, he puesto que cargue cada 0.5 s
#include "C:\Users\xx\Desktop\VELOCIDAD\vel2.h"
#include "C:\Users\xx\Desktop\flex_lcd420.c"
#FUSES MCLR
int16 contador=0;
int1 flag=0;
int16 pulsos=0;
float RPM=0;
float VEL=0;
#int_TIMER1
void TIMER1_isr(void)
{
set_timer1(3036); //Carga del TMR1
Contador++;
flag=1;
}
#int_CCP1
void CCP1_isr(void)
{
Pulsos++;
}
void main()
{
Set_tris_C(0x04); //C2 ENTRADA
lcd_init();
printf(lcd_putc, "VELOCIMETRO");
lcd_gotoxy(1,2);
printf(lcd_putc,"VELOCIDAD KM/H");
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_ccp1(CCP_CAPTURE_RE);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if (flag==1){
RPM=pulsos*10*60; //Overflow de 0.1s
VEL=RPM*0.08; //RADIO ES DE 8 CM
printf(lcd_putc,"%61u RPM",RPM);
lcd_gotoxy(1,2);
printf(lcd_putc,"%61u VEL",VEL);
flag=0;
}
}
}
Mi timer el overflow maximo que he encontrado es de 0.1, por eso multiplico por 10 en las rpm
-
El printf dejalo dentro.
Ahora vamos a las unidades y formulas:
printf(lcd_putc,"VELOCIDAD KM/H");
VEL=RPM*0.08;
La longitud de la circunferencia es igual a 2 * pi * radio
asi que VEL = RPM * 2 * pi * 0.08m
aproximadamente
VEL = RPM(vuelta/min) * 0.502654(m/vuelta)
Pero es en m/min, deberias dividirlo sobre 1000 para que te den los km/min y luego multiplicarlo por 60 para que quede como km/h
Vel (m/min) * 1/1000 (km/m) * 60/1 (min/h) = Vel (km/h)
VEL = RPM * 0.030159089 // Formula final para que de Km/h
-
Seguí tus instrucciones y el velocímetro funciona correctamente, el único problema que tengo es que cuando la rueda deja de girar se muestra el valor máximo que ha alcanzado, es decir, que cuando disminuye la velocidad no disminuye o no lo muestra por pantalla se queda el valor mas grande que haya alcanzado hasta ese momento. He estado tocando cosas del programa pero no consigo arreglarlo. También he variado y en lugar de hacerlo con un while lo he hecho con un do
#include <18F4550.h>
#use delay(clock=20000000)
#include "C:\Users\xxx\Desktop\libreria_lcd\flex_lcd420.c"
#include <math.h>
int16 contador=0;
int1 flag=0;
int16 pulsos=0;
float RPM=0;
float VEL=0;
#int_TIMER1
void TIMER1_isr(void)
{
Contador++;
flag=1;
set_timer1(0);//Reinicia la cuenta
}
#int_CCP1
void CCP1_isr(void)
{
Pulsos++;
}
void main()
{
Set_tris_C(0x04); //C2 ENTRADA
lcd_init();
printf(lcd_putc, "VELOCIMETRO");
lcd_gotoxy(1,2);
printf(lcd_putc,"VELOCIDAD KM/H");
delay_ms(2000);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_ccp1(CCP_CAPTURE_RE);
set_timer1(0); //Borrado contador
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
do
{
if (flag==1){
RPM=pulsos*10*60; //Overflow de 0.1s
VEL=RPM*0.030159089; //RADIO ES DE 8 CM
lcd_putc('\f');
printf(lcd_putc,"Vel = %0f Km/h",VEL);
lcd_gotoxy(1,1);
delay_ms(1000);
flag=0;
}
}while(true);
}
-
Bueno hay un problema ahi, estas usando un delay dentro de la funcion. Esto hace que cuando muestre tenga muchos mas pulsos acumulados de los que deberia, Con lo que el valor deberia ser incorrecto.
Por lo que veo queres que se actualize cada 1 segundo... Entonces te propongo un par de cambios:
#use delay(clock=20000000)
#include "C:\Users\xxx\Desktop\libreria_lcd\flex_lcd420.c"
#include <math.h>
int16 contador=0;
int1 flag=0;
int16 pulsos=0;
float RPM=0;
float VEL=0;
#int_TIMER1
void TIMER1_isr(void)
{
Contador++;
if(Contador==10)
{
flag=1;
Contador=0;
}
set_timer1(0);//Reinicia la cuenta
}
#int_CCP1
void CCP1_isr(void)
{
Pulsos++;
}
void main()
{
Set_tris_C(0x04); //C2 ENTRADA
lcd_init();
printf(lcd_putc, "VELOCIMETRO");
lcd_gotoxy(1,2);
printf(lcd_putc,"VELOCIDAD KM/H");
delay_ms(2000);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_ccp1(CCP_CAPTURE_RE);
set_timer1(0); //Borrado contador
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
do
{
if (flag==1){
RPM=pulsos*60; //Activa el flag cada 10 * 0.1s, se guardan los pulsos por 1 segundos
pulsos=0;
VEL=RPM*0.030159089; //RADIO ES DE 8 CM
lcd_putc('\f');
printf(lcd_putc,"Vel = %0f Km/h",VEL);
lcd_gotoxy(1,1);
flag=0;
}
}while(true);
}
Puse en negrita lo que cambie, antes guardabamos la cantidad de pulsos cada 0.1s y haciamos los calculos, primero multilpicabamos por 10 y luego por 60.
Ahora directamente observamos cuantos pulsos hay en 1 segundo (el timer sigue siendo de 0.1s pero no activa el flag hasta que Contador sea igual a 10, es decir 1 segundo)
Una ves utilizada la formula que usa la variable "pulsos" ya no nos interesa saber el valor que tenga,asi que procedemos a borrar la cantidad de pulsos, para poder comenzar nuevamente.
Respecto al do ..while y el while.
Casi no hay diferencias entre estos, la unica diferencia es que el do..while se ejecuta y luego observa la condicion, mientras que el while hace lo contrario, observa la condicion si se cumple ejecuta el codigo
do
{
PrenderLed();
ApagarLed();
} while (false);
Suponiendo que tenemos unas funciones asi. Ese codigo haria que el led se encienda y luego se apague y al no cumplir se la condicion no se sigue ejecutando
While(false)
{
PrenderLed();
ApagarLed();
}
Este ultimo codigo nunca se ejecutaria, ni siquiera una sola ves.
Para lo que nos compete, es exactamente lo mismo, ya que tratamos de crear un bucle infinito, pero uso el while por que es mas corto.