if(INPUT(PIN_A0)==1) //condición de la pulsación de incremento
{
P_RPM=200;
i=P_RPM/30; //cantidad de pulsos que hay que contar por minuto para poder tener las revoluciones requeridas, como el encoder es de 2 pulsos por vuelta entonces 60/2=30
while(contador<i)
{
prueba++;
set_pwm1_duty(prueba);//no se si está bien plantearlo así
}
}#INCLUDE <16F876A.h>
#DEVICE ADC=10
#USE DELAY(CLOCK=4000000)
#FUSES XT,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
#INCLUDE <flex_lcd.C>
#DEFINE USE_PORTB_LCD TRUE
//Direcciones de memoria que utilizan los puertos A,B y C
#BYTE PORTA= 5
#byte PORTB= 6
#byte PORTC= 7
long contador=0;//Variable para contar los pulsos del encoder
int16 duty=0;//para guardar el ciclo duty del pwm
int Timer2,Poscaler;//para configurar el pwm
double RPM,P_RPM;//se guarda las revoluciones por minuto del motor
int i=0; //variable que servira como contador
int prueba=0;
//Interrubción por cambio en RB0
#INT_EXT
ext_isr()//esta funcion solo se activa cuando hay una interrupcion por flanco de subida (L TO H)
{
contador++;
}
void main()
{
//Configura los Puertos del PIC
set_tris_a(0b00000111);//RA0 RA1 RA2 como entrada, donde está el potenciometro
set_tris_b(0b00000011); //RB0 como entrada, donde está conectado el encoder del motor y se utiliza interrupcion externa (que trabaja con este pin)
set_tris_c(0b00000000);//todo el puerto C como salida
lcd_init();
lcd_gotoxy(1,1);
printf(LCD_PUTC, "BIENVENIDO");
delay_ms(200);
//Habilita las Interrupcciones
enable_interrupts(int_ext); //Activa interrupcion por RB0
ext_int_edge(L_TO_H); //Activa interrupción por flanco de subida
enable_interrupts(global); //Habilita interrupciones de forma global, se hacen arriba del main como funcion global
//Configura el PWM
// Generemos una Señal cuadrada de 1 Khz
Timer2=249; //Se carga timer 2 con 249 como lo vimos en la pagina
Poscaler=1; //Preescaler solo puede tomar valores de: 1
setup_timer_2(t2_div_by_4,Timer2,Poscaler); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz
setup_ccp1(ccp_pwm); //Configura el modulo CCP1 en modo PWM
set_pwm1_duty(70); //Determina el ciclo de trabajo en escala de 0 a 255 (PARAMETRO DE INICIO)
//Hace por siempre
while(1)
{
if(INPUT(PIN_A0)==1) //condicion de la pulsacion de incremento
{
P_RPM=200;
i=P_RPM/30; //cantidad de pulsos que hay que contar por minuto para poder tener las revoluciones requeridas
while(contador<i)
{
prueba++;
set_pwm1_duty(prueba);
}
}
if(INPUT(PIN_A1)==1) //condicion de la pulsacion de incremento
{
}
if(INPUT(PIN_B1)==1) //condicion de la pulsacion de incremento
{
set_pwm1_duty(0);
i=0;
}
//Espera por 1 segundo*/
delay_ms(999);
//Despues de 1 segundo multiplica el valor de los pulsos contados en
//contador y los multiplica por 60 (para pasarlo a minutos) y lo divide
//por el numero de pulsos que tiene el encoder, en este caso el encoder
//da 10 pulsos en 1 giro completo
RPM=contador*60/2;
// Reinicia el contador de Pulsos
contador=0;
// Visualiza la Velocidad
lcd_gotoxy(1,2);
printf(lcd_putc,"RPM: %f ",RPM);
}
}Hola. Lo que tienes hecho entonces te está funcionando ¿no?.
No está mal planteado. La diferencia es que tú determinas las rpm contando pulsos que al ser un encoder de varios dientes tienes varios pulsos por rpm y por lo tanto más resolución. Pero a mi personalmente y habiendo timer libres siempre he preferido obtener el periodo y no contar pulsos. Depende de la precisión y velocidad de respuesta del programa que quieras, pero en principio como lo planteas es válido.
Un saludo.
Ahí lo que pasa es que ponerte a contar pulsos hace muy lento tu programa y hace que las correcciones sucedan cada segundo a saltos. No es lo suyo.
Lo ideal y para que el programa sea rápido y fluido, tanto que el motor te haga caso inmediatamente al tocar el pulsador + o el - es tener un bucle infinito en main donde lo único que haces es leer pulsadores y gobernar el pwm. Nada más.
Lo demás se hace en el handler de interrupción al que se entra por cambio de estado del pin del encoder como ya haces, pero en vez de contar pulsos, como tú ya has configurado la interrupción para que entre en el flanco de subida pues lo que yo haría dentro de ese handler de forma basica es:
- Lo primero desactivar las interrupciones para que no haya reentrada.
- mantener a la espera del flanco de bajada:Código: C// ya tienes un periodo que es exactamente el tiempo que ha tardado en pasar una cresta y un valle completo del encoder. Con eso es suficiente.
while (input(PIN_B0); // esperando flanco de bajada. set_timer3(0); //. Pongo el “crono” a cero. while(!input(PIN_B0); // Espero flanco de subida. while(input(PIN_B0); // Espero flanco Bajada. int16 periodo = get_timer3(); // capturo el valor del timer
Ahora ese valor, que es una cuenta de un timer lo pasas a tiempo. Para calcular el tiempo tienes que saber:
- La velocidad del oscilador. La frecuencia de entrada al timer es fosc/4, es decir,’si tu micro va a 20 Mhz, la frecuencia de entrada al timer es 20/4 = 5 Mhz.
- El prescaler del timer. Si le has puesto por ejemplo 1/8, la salida del timer será 5 Mhz/8 = 625 Khz.
Significa que en un segundo el Timer cuenta de 0 a 625000 y que tardaría en hacer un incremento = 1/625000 = 1,6 uS.
( Aquí viene la importancia de contar los desbordamientos) por que el timer3 en modo 16 bits solo puede contar hasta 65535 + 1. Por lo que en un segundo te va a desbordar 625000/65536 = 9 veces, y claro, ¿como totalizo todo eso?, pues contando las veces que desborda durante las esperas de los flancos arriba en los while. Por lo que los while de arriba quedarían asi:Código: CUna vez recibido el flanco de bajada capturamos inmediata el valor actual del timer:
while (input(PIN_B0); // esperando flanco de bajada. set_timer3(0); //. Pongo el “crono” a cero. int overflow = 0; // Para contar los desbordamientos del timer. while(!input(PIN_B0) { // Espero flanco de subida. if (tmr3if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador tmr3if = 0; // bajo la bandera y continuo esperando flanco. } // Y ahora lo mismo para el otro flanco: while(input(PIN_B0) { // Espero flanco Bajada. if (tmr3if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador tmr3if = 0; // bajo la bandera y continuo esperando flanco. }Código: CY ahora a ese valor le sumamos todos los desbordamientos que hayan habido.
float periodo = get_timer3(); // capturo el valor del timerCódigo: CAhora lo pasamos a tiempo:
periodo += (65535 + 1) * overflow;Código: CHaces la inversa:
Periodo *= 0.00000165; // Todo lo que hemos cronometrado con el timer * 1,65 uS.Código: C
periodo = 1/periodo; // ahora tienes la frecuencia de un solo diente del encoder. (Valle y cresta) periodo *= número de (dientes + valles); // Ya tienes el total de lo que tardarían todos los dientes y valles en dar un giro completo. return periodo * 60; //. Retorna el valor en rpm
// fin de handler de interrupción.
Ahora ya en el bucle del main compáralo y vas corrigiendo.
Hazlo todo por bloques, es decir, primero haz girar el motor con tensión externa y que el programa sea capaz de determinar sus rpm.
Ya luego el control, lo que haría básicamente es:
¿ Cuantas rpm quiero? vset.
¿ A cuanto gira? v_current.
¿Diferencia? signed int diferencia = vset - v_current;
Si sale positivo, reduces las rpm en la proporción exacta de la diferencia.
Si sale negativo, aumentas las rpm en la proporción.
Y así sucesivamente....
#INCLUDE <16F876A.h>
#DEVICE ADC=10
#USE DELAY(CLOCK=4000000)
#FUSES XT,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
#INCLUDE <flex_lcd.C>
#DEFINE USE_PORTB_LCD TRUE
//Direcciones de memoria que utilizan los puertos A,B y C
#BYTE PORTA= 5
#byte PORTB= 6
#byte PORTC= 7
long contador=0;//Variable para contar los pulsos del encoder
int16 duty=0;//para guardar el ciclo duty del pwm
int Timer2,Poscaler;//para configurar el pwm
double RPM,P_RPM;//se guarda las revoluciones por minuto del motor
int i=0; //variable que servirá como contador
//Interrupción por cambio en RB0
#INT_EXT
ext_isr()//esta función solo se activa cuando hay una interrupción por flanco de subida (L TO H)
{
while (input(PIN_B0)); // esperando flanco de bajada.
set_timer3(0); //. Pongo el “crono” a cero.
int overflow = 0; // Para contar los desbordamientos del timer.
while(!input(PIN_B0))
{ // Espero flanco de subida.
if (tmr3if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador
tmr3if = 0; // bajo la bandera y continuo esperando flanco.
}
// Y ahora lo mismo para el otro flanco:
while(input(PIN_B0))
{ // Espero flanco Bajada.
if (tmr3if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador
tmr3if = 0; // bajo la bandera y continuo esperando flanco.
}
float periodo = get_timer2(); // capturo el valor del timer
periodo += (65535 + 1) * overflow;
periodo *= 0.00000165; // Todo lo que hemos cronometrado con el timer * 1,65 uS.
periodo = 1/periodo; // ahora tienes la frecuencia de un solo diente del encoder. (Valle y cresta)
periodo *= 2; // el motor es de 2 pulsos
RPM=periodo * 60; //. Retorna el valor en rpm
}
void main()
{
//Configura los Puertos del PIC
set_tris_a(0b00000111);//RA0 RA1 RA2 como entrada, donde está el potenciometro
set_tris_b(0b00000011); //RB0 como entrada, donde está conectado el encoder del motor y se utiliza interrupcion externa (que trabaja con este pin)
set_tris_c(0b00000000);//todo el puerto C como salida
lcd_init();
lcd_gotoxy(1,1);
printf(LCD_PUTC, "BIENVENIDO");
delay_ms(200);
//Habilita las Interrupcciones
enable_interrupts(int_ext); //Activa interrupcion por RB0
ext_int_edge(L_TO_H); //Activa interrupción por flanco de subida
enable_interrupts(global); //Habilita interrupciones de forma global, se hacen arriba del main como funcion global
//Configura el PWM
// Generemos una Señal cuadrada de 1 Khz
Timer2=249; //Se carga timer 2 con 249 como lo vimos en la pagina
Poscaler=1; //Preescaler solo puede tomar valores de: 1
setup_timer_2(t2_div_by_4,Timer2,Poscaler); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz
setup_ccp1(ccp_pwm); //Configura el modulo CCP1 en modo PWM
set_pwm1_duty(70); //Determina el ciclo de trabajo en escala de 0 a 255 (PARÁMETRO DE INICIO)
//Hace por siempre
while(1)
{
// Visualiza la Velocidad
lcd_gotoxy(1,2);
printf(lcd_putc,"RPM: %f ",RPM);
}
}Espero que me puedas ayudar, desde ya muchas graciasPor que tu micro no tiene timer3. Por eso te da fallo el set_timer3(0);Hola remi
El timer2 es para el PWM, así que usa el timer1 en modo 16 bits. y cambia los set_timer3, por set_timer1.
Respecto a la bandera tmr1if tienes que definirla colocando la siguiente macro en la cabecera:Código: C
#bit tmr2if = 0x00c.0
Y no olvides colocar un disable interrupt global al principio del handler y un Enable al salir para reanudarlas.
#INCLUDE <16F876A.h>
#DEVICE ADC=10
#USE DELAY(CLOCK=4000000)
#FUSES XT,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
#INCLUDE <flex_lcd.C>
#DEFINE USE_PORTB_LCD TRUE
//Direcciones de memoria que utilizan los puertos A,B y C
#BYTE PORTA= 5
#byte PORTB= 6
#byte PORTC= 7
#bit tmr2if = 0x00c.0
long contador=0;//Variable para contar los pulsos del encoder
int16 duty=0;//para guardar el ciclo duty del pwm
int Timer2,Poscaler;//para configurar el pwm
double RPM,P_RPM;//se guarda las revoluciones por minuto del motor
int i=0; //variable que servira como contador
//Interrubción por cambio en RB0
#INT_EXT
ext_isr()//esta funcion solo se activa cuando hay una interrupcion por flanco de subida (L TO H)
{
disable_interrupts(global);
while (input(PIN_B0)); // esperando flanco de bajada.
set_timer1(0); //. Pongo el “crono” a cero.
int overflow = 0; // Para contar los desbordamientos del timer.
while(!input(PIN_B0))
{ // Espero flanco de subida.
if (tmr2if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador
tmr2if = 0; // bajo la bandera y continuo esperando flanco.
}
// Y ahora lo mismo para el otro flanco:
while(input(PIN_B0))
{ // Espero flanco Bajada.
if (tmr2if) overflow ++; // ¿Ha desbordado el timer3?, incremento el contador
tmr2if = 0; // bajo la bandera y continuo esperando flanco.
}
float periodo = get_timer1(); // capturo el valor del timer
periodo += (65535 + 1) * overflow;
periodo *= 0.00000165; // Todo lo que hemos cronometrado con el timer * 1,65 uS.
periodo = 1/periodo; // ahora tienes la frecuencia de un solo diente del encoder. (Valle y cresta)
periodo *= 2; // el motor es de 2 pulsos
RPM=periodo * 60; //. Retorna el valor en rpm
}
void main()
{
//Configura los Puertos del PIC
set_tris_a(0b00000111);//RA0 RA1 RA2 como entrada, donde está el potenciometro
set_tris_b(0b00000011); //RB0 como entrada, donde está conectado el encoder del motor y se utiliza interrupcion externa (que trabaja con este pin)
set_tris_c(0b00000000);//todo el puerto C como salida
lcd_init();
lcd_gotoxy(1,1);
printf(LCD_PUTC, "BIENVENIDO");
delay_ms(200);
//Habilita las Interrupcciones
enable_interrupts(int_ext); //Activa interrupcion por RB0
ext_int_edge(L_TO_H); //Activa interrupción por flanco de subida
enable_interrupts(global); //Habilita interrupciones de forma global, se hacen arriba del main como funcion global
//Configura el PWM
// Generemos una Señal cuadrada de 1 Khz
Timer2=249; //Se carga timer 2 con 249 como lo vimos en la pagina
Poscaler=1; //Preescaler solo puede tomar valores de: 1
setup_timer_2(t2_div_by_4,Timer2,Poscaler); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz
setup_ccp1(ccp_pwm); //Configura el modulo CCP1 en modo PWM
set_pwm1_duty(70); //Determina el ciclo de trabajo en escala de 0 a 255 (PARAMETRO DE INICIO)
//Hace por siempre
while(1)
{
// Visualiza la Velocidad
lcd_gotoxy(1,2);
printf(lcd_putc,"RPM: %f ",RPM);
}
}
Hola, he estado ausentado unos días.
¿Puedes poner una foto del encoder del motor? y como lo estas conectando.
Por ejemplo, el esquemático de proteus (No lo tengo instalado). Si es el mismo que el del primer mensaje hay que ver el tipo de encoder y la señal que devuelve, si es una señal 0-5V o si es open drain (Caso mas probable).
Si es open drain y lo tienes conectado como aparece en el diagrama del primer mensaje, no va a funcionar salvo que habilites las resistencias pullup internas del puerto B, y esto se hace poniendo a cero el bit 7 del registro option_reg. Definelo con la siguiente macro:Código: C
#bit RBPU=0x81.7
Luego en el main pones:Código: CY empezar desde abajo del todo, lo primero, estar seguro de que el encoder está funcionando con un bucle de prueba, y un led conectado por ejemplo al portb4 por ejemplo:
RBPU = 0; // Pullup en puertos RB3 a RB0 activados.Código: C
output_low(PIN_B4); // Establece el puerto b4 como salida y a nivel bajo. (Led conectado a este puerto con su resistencia de limitación). while(1) { // Loop. if (input(PIN_B0)) output_high(PIN_B4); else output_low(PIN_B4); }
Desactiva las interrupciones.
Con este código puesto en tu main justo despues de realizar todas las configuraciones del micro, el led debe encender y apagarse conforme gires el motor con la mano.
Hola, he estado ausentado unos días.
¿Puedes poner una foto del encoder del motor? y como lo estas conectando.
Por ejemplo, el esquemático de proteus (No lo tengo instalado). Si es el mismo que el del primer mensaje hay que ver el tipo de encoder y la señal que devuelve, si es una señal 0-5V o si es open drain (Caso mas probable).
Si es open drain y lo tienes conectado como aparece en el diagrama del primer mensaje, no va a funcionar salvo que habilites las resistencias pullup internas del puerto B, y esto se hace poniendo a cero el bit 7 del registro option_reg. Definelo con la siguiente macro:Código: C
#bit RBPU=0x81.7
Luego en el main pones:Código: CY empezar desde abajo del todo, lo primero, estar seguro de que el encoder está funcionando con un bucle de prueba, y un led conectado por ejemplo al portb4 por ejemplo:
RBPU = 0; // Pullup en puertos RB3 a RB0 activados.Código: C
output_low(PIN_B4); // Establece el puerto b4 como salida y a nivel bajo. (Led conectado a este puerto con su resistencia de limitación). while(1) { // Loop. if (input(PIN_B0)) output_high(PIN_B4); else output_low(PIN_B4); }
Desactiva las interrupciones.
Con este código puesto en tu main justo despues de realizar todas las configuraciones del micro, el led debe encender y apagarse conforme gires el motor con la mano.