TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: Xa2 en 21 de Julio de 2004, 03:14:00
-
Buenas gente, haber si podeis ayudarme!!
Deseo generar un duty cycle dependiendo de la velocidad que queremos que gire, es decir, se introduce la velocidad en r.p.m. y se transmite via serie, entonces el PIC generara un PWM dependiendo de esta velocidad, la regla de tres es la siguiente
(velocidad*100)/3900.0=el resultado es el duty cycle que queremos; luego logicamente nos daria una velocidad, que muy dificilmente coincidiria con la deseada, entonces habria que sacar el error y multiplicarlo por una constante y enviar de nuevo otro duty, en funcion del error y la constante (en resumen un lazo Proporcional)!!!
Pero mi primera duda es la primera, la segunda ya se intentaria solucionr mas adelante!!
Aqui os mando lo que he hecho, pero no funciona correctamente!!!
Creo que es por alguna definicion de variables, pero si mem pudierais ayudar mejor!!
codigo:
#include <16f873.h>
#device adc=10
#include <stdlib.h>
#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8)
#zero_ram
int opcion;
int i;
int16 duty;
int16 pulsos;
int16 interrupcion;
int16 velocidad;
int16 recibido;
int reset;
float dato;
//float const resolucion=5.0/1024.0;
int16 value;
char string[10];
//float corriente;
//float sumacorriente;
//float corrientemedia;
int habilitacion;
//int convertidor;
int32 tiempoReal;
int32 Tiempo;
#int_RTCC //se determina la funcion de la interrupcion
RTCC_isr()
{
if(habilitacion==1)
{
tiempoReal++; //se suma a una variable que computa el tiempo transcurrido
pulsos=get_timer1(); //se obtiene el valor del contador timer1
interrupcion++; //se suma en uno el flag de interrupcion
if(interrupcion==100) //cuando se llega a 100, es decir, 1 segundo, 100*10ms=1000ms=1seg; se introduce en la funcion
{
velocidad=pulsos*15; //sacamos la velocidad con una regla de tres
printf("%lu r.p.m.
",velocidad); //tambien puerto serie
/* ************ convertidor analógico *****************
for(i=0;i<10;i++)
{
set_adc_channel( 0 ); //el canal 0, entrada AN0
delay_us(160); //delay 1us
value = read_adc();
dato = value*resolucion;
corriente=dato; //una R=0,005 --> V=IR; I=V/R; corriente=dato/R=dato/0.005=corriente
sumacorriente=sumacorriente+corriente;
corrientemedia=sumacorriente/10.0;
}
printf("%1.3f A
",corrientemedia);*/
printf("%lu duty",duty);
Tiempo=tiempoReal*0.01;
printf("%lu segundos", Tiempo);
//corrientemedia=0;
//sumacorriente=0;
interrupcion=0; //se inicializa de nuevo el flag de interrupcion para que vuelva a contar 100 interrupciones
set_timer1(0); //se inicializa tambien el contador
set_rtcc(178); //se introduce de nuevo el valor del temporizador de interrupcion
}
else
{
set_rtcc(178);
set_timer1(pulsos);
}
}
}
main()
{
//set_tris_b(0x00); //si se habilita no se puede sacar el PWM 1
enable_interrupts(GLOBAL); //se habilitan interrupcion general
enable_interrupts(INT_RTCC); //habilita int. del timer0
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128); //se configura el timer0 como temporizador y una báscula de 128
set_rtcc(178); //10ms -> 10000us -> 10000/128=78 -> 255-78=177 se le inserta el tiempo a contar, se suma uno
duty = 0; //se inicializa el duty en cero
setup_ccp1(CCP_PWM); //Se habilita el pwm 1
setup_timer_2(T2_DIV_BY_16, 99, 1); //Se establece el valor de la frecuencia de 620Hz(1,6 ms el periodo) para el PWM
set_pwm1_duty(duty); //se manda el duty al PWM
//setup_adc_ports( ALL_ANALOG ); //todo el puerto analogico
//setup_adc( ADC_CLOCK_INTERNAL ); //convertidor con clock interno
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1); //se configura el timer1 como contador, de pulsos externos y de báscula 1
set_timer1(0); //se inicializa el contador en cero
interrupcion=0; //se inicializan los flags
habilitacion=0;
//convertidor=0;
tiempoReal=0;
Tiempo=0;
while(true)
{
//el programa principal que estaria haciendo, esperando a la interrupcion del timer0
//while(!kbhit());
gets(string); //Guarda el Duty (0-255) en la variable string aunque solo necesitamos hasta 100
//y espera un RETURN (Ctrl 13)
opcion=atoi(string); //Usa la función ATOI para pasarlo a entero
switch (opcion) {
case 3: //recibido duty cycle
while(true)
{
gets(string);
recibido=atol(string);
if(recibido==200 || recibido==201);
reset_cpu();
else
duty=recibido;
set_pwm1_duty(duty); //El caracter ascii recibido pasado a entero
//establecido como ciclo de trabajo
habilitacion=1; //flag pata habilitar el contaje de los pulsos de velocidad del optoacoplador
}
break;
case 4: //recibido velocidad
while(true)
{
gets(string);
recibido=atol(string);
if(recibido==200 || recibido==201)
reset_cpu();
else
duty=(recibido*100.0)/3900.0;
set_pwm1_duty(duty);
habilitacion=1;
}
break; }
}
}
PD: en el codigo estoy intentando tambien tener la opcion de poder controlar con la velocidad o con el duty cycle; por eso la sentencia CASE!! Dpendiendo de lo que le llegue por el puerto serie (un 3 o un 4)!!
Muchas gracias y saludos!!!
-
Buenas de nuevo!!
Acabo de solucionar el problema, ya puedo controlar mediante el duty cycle y la velocidad si lo deseo, dependiendo de lo que me llegue por el puerto serie!!
Ahora solo me falta realizar el lazo cerrado Proporcional, si alguien podria ayudarme, es uqe no tengo ni idea de como se podria realizar.
Este lazo solo se podria realizar en el control mediante velocidad, no en el control mediante duty!!
Entonces yo solo se que tengo sacar el error, es decir, la diferencia entre la velocidad medida y la deseada, y luego ese error multiplicarlo por una constante ygenerar otro duty, y asi ir acercandonos al velocidad deseada!!
Eso enteoria, pero como se podria hacer programando??? Como se calcularia la konstante, por la cual hay que multiplicar???
Gracias y saludos, os pongo el nuevo codigo:
#include <16f873.h>
#device adc=10
#include <stdlib.h>
#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8)
#zero_ram
int opcion;
int i;
int duty;
int16 pulsos;
int16 interrupcion;
int16 velocidad;
int16 recibido;
int reset;
float dato;
int16 value;
char string[10];
int habilitacion;
int32 tiempoReal;
int32 Tiempo;
#int_RTCC //se determina la funcion de la interrupcion
RTCC_isr()
{
if(habilitacion==1)
{
tiempoReal++; //se suma a una variable que computa el tiempo transcurrido
pulsos=get_timer1(); //se obtiene el valor del contador timer1
interrupcion++; //se suma en uno el flag de interrupcion
if(interrupcion==100) //cuando se llega a 100, es decir, 1 segundo, 100*10ms=1000ms=1seg; se introduce en la funcion
{
velocidad=pulsos*15; //sacamos la velocidad con una regla de tres
printf("%lu ",velocidad); //tambien puerto serie
printf("%u ",duty); //imprimo el duty para saber cual nos ha generado
Tiempo=tiempoReal*0.01; //calculo el tiempo en segundos trnascurrido
printf("%lu ", Tiempo); //imprimo el tiempo
interrupcion=0; //se inicializa de nuevo el flag de interrupcion para que vuelva a contar 100 interrupciones
set_timer1(0); //se inicializa tambien el contador
set_rtcc(178); //se introduce de nuevo el valor del temporizador de interrupcion
}
else
{
set_rtcc(178);
set_timer1(pulsos);
}
}
}
main()
{
enable_interrupts(GLOBAL); //se habilitan interrupcion general
enable_interrupts(INT_RTCC); //habilita int. del timer0
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128); //se configura el timer0 como temporizador y una báscula de 128
set_rtcc(178); //10ms -> 10000us -> 10000/128=78 -> 255-78=177 se le inserta el tiempo a contar, se suma uno
duty = 0; //se inicializa el duty en cero
setup_ccp1(CCP_PWM); //Se habilita el pwm 1
setup_timer_2(T2_DIV_BY_16, 99, 1); //Se establece el valor de la frecuencia de 620Hz(1,6 ms el periodo) para el PWM
set_pwm1_duty(duty); //se manda el duty al PWM
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1); //se configura el timer1 como contador, de pulsos externos y de báscula 1
set_timer1(0); //se inicializa el contador en cero
interrupcion=0; //se inicializan los flags
habilitacion=0;
tiempoReal=0;
Tiempo=0;
while(true)
{
//el programa principal que estaria haciendo, esperando a la interrupcion del timer0
//while(!kbhit());
gets(string); //Guarda el Duty (0-255) en la variable string aunque solo necesitamos hasta 100
//y espera un RETURN (Ctrl 13)
opcion=atoi(string); //Usa la función ATOI para pasarlo a entero
switch (opcion) { //case para entrar en un ocdigo u otro
case 3: //recibido duty cycle se controla mediante duty cycle con un rango
while(true)
{
gets(string); //recibo la cadena
recibido=atol(string); //la convierto la cadena a entero
if(recibido==150 || recibido==151) //si se envia cualquiera de estos dos numeros se resetea el PIC, reset por software
{
reset_cpu();
}
else //si no se ha recibido reset o desconexion, se prosigue a realizar el duty
{
duty=recibido;
set_pwm1_duty(duty); //El caracter ascii recibido pasado a entero
//establecido como ciclo de trabajo
habilitacion=1; //flag pata habilitar el contaje de los pulsos de velocidad del optoacoplador
}
}
break;
case 4: //recibido velocidad se controla mediante velocidad con un rango
while(true)
{
gets(string);
recibido=atol(string);
if(recibido==150 || recibido==151)
{
reset_cpu();
}
else //si no se ha recibido el reset o desconexion se prosigue a ejecutar el duty
{
duty=(recibido*100.0)/3900.0;
set_pwm1_duty(duty);
habilitacion=1;
}
}
break; } //fin del "CASE"
}
}
-
Xa2 me as dejado
estoy intentando acer algo parecido a esto e me e passado meses e nada de nada e de repente aparece un genio e en dos dias lo consigue
que dezirte bueno que eres un genio e perguntarte si puedo usar parte de tu codigo para mi bichito que estoy construiendo ace 4 meses e no me salia ni por nada
-
Gracias asi da gusto!!
Claro que puedes utilizarlo parte, pero hay que cambiar 150 y 151 por 5000 y 5001, ya que si introduces una velocidad de 150 o 151 el PIC se resetea!!!
Saludos!!
-
Te felicito Xa2, con la información que te dimos y con lo que tu has añadido te ha quedado un proyecto chulo.
Ahora a ver como solucionas lo de los decimales en el Duty de la velocidad ... estoy seguro que lo conseguirás.
Un saludo
-
Gracias Pocher!!!
Todo un cumplido viniendo de ti, muchas gracias!!!
No tengo el problema del duty, mi problema es otro, es el lazo proporcional!!!
Que no se que calculos hay que hacer ni nada!!
Haber si podeis ayudarme o mandarme alguna informacion buena!!
Gracias y saludos!!!
-
Sí creo que sí que son los decimales. Mira un ejemplo:
Si mandas una v=60 r.p.m. ---> CCPR1L (Duty)=(60·100)/3900=1,5 entonces como la variable duty es entera toma 1 y claro ya no irá a 60 r.p.m. irá a menos
Si mandas una v=180 r.p.m. ---> CCPR1L (Duty)=(180·100)/3900=4,6 entonces como la variable duty es entera toma 4 y claro ya no irá a 180 r.p.m. irá a menos
PD. ¿Elegiste 4 radios en el disco, no?
-
Si elegi 4 radios en el disco!!
Pero si tengo decimales, no puedo mandar flotantes para el PWM, solo enteros, entonces como se mandaria con decimales??
De todas formas luego vendria el lazo cerrado de velocidad proporcional y se corregiria el error o diferencia de velocidades deseadas y obenidas!!
No se contestame que te parece, o que harias tu!!!
Gracias de nuevo, no se que haria sin ti, jejej y luego dicen que soy un genio, pero si aqui el unico genio eres tù!!!
Saludos
-
Entre todos vamos haciendo camino ...
No te prometo nada, si puedo miraré lo del control P.
Un saludo
-
Oye si tienes el "chiringuito" montado podrías comprobar y decirme que velocidades devuelve si le envias por ejemplo 60 r.p.m., 120 r.p.m., 180 r.p.m. y 3900 r.p.m.
-
Pues ahora mismo no, porque esoty haciendo la placa y estoy teniendo unos problemillas, pero te puedo decir que la minima velocidad es de 700 r.p.m., ya que cuando introduces menos el motor no gira, no le llega suficiente intensidad!!
Si te sirve de ayuda!!
Cuando termine de monatr, te dejo el resto de datosQ!Q
Saludos^Pocher !!!
-
Ya esta, a 3900 r.p.m. me dan 3600 r.p.m. alrededor, siempre por esos valores!!
Las demas velocidades no me da!!
Tu me diras!!
Saludos
-
Pués sí que se va de mucho y eso que para 3900 r.p.m. no hay decimales (sale un duty justo igual a 100).
¿Porqué en esta medida se va de tanto? Prueba a conectar el motor directamente a alimentación (es de 5V, 12V, ... ¿?) a ver si saca las 3900 r.p.m. (velocidad máxima especificada en el motor), ya me dirás cuanto sale ahora.
-
Nosotros lo alimentamos a 13,2 V; pero te voy a decir porque cuando pongo 3900 r.p.m. deseadas salen 3600; es porque en el programa hago una regla de tres, y he supuesto que al 100 de duty la velocidad es de 3900, cuando es alrededor de 3600; por eso sale la diferencia, si se pone en el programa entre 3700 donde pone entre 3900; al desear 3700 r.p.m. nos saldria unas 3680 o siempre por esos valores.
Por eso nos sucede ese error!!
Creo que es por eso, si te vale la respuesta??
Saludos!!!
-
Joder se me ha olvidado decirte que en el motor tenemos una resistencia shunt de 1 ohmio; entonces hay una caida de tension de como maximo de 120 mV; esta medido, por lo que al motor le llegan 13,1 V mas o menos; te lo digo para que lo sepas aunque no creo que influya en la diferencia de velocidad real y velocidad del motor deseada!!
Yo creo que es por lo que te he dicho antes!!
Seguire probando cambiando la regla de tres que te he dicho, y te dare los resultados!
Saludos!!!
-
Claro, claro en el programa tienes que cambiar el 3900 por la velocidad máxima real medida, si no lo estarías engañando.
-
Una vez solucionado esto, haber si me pdeis ayudar con el problema del control proporcional, que estoy desesperado!!!
Haber si me facilitais por lo menos informacion para que la trabaje yo; o algo asi, no os pido el codigop hecho, solo posible ayuda para ir haciendo algo.
Saludos y espero que podais, pero si no tranquilos eh???
Gracias!!!
-
Ahhhh!!!!
Pocher te tengo que decir que la variacion de la velocidad del motor medida, respecto del duty introducido no es lineal, es exponencial!!!
Te pongo los datos para que los veas mejor
Duty Velocidad medida
19 Nada
20 135
21 300
22 480
23 645
25 945
30 1605
35 2100
40 2460
45 2730
50 3000
55 3030
60 3270
65 3300
70 3330
75 3375
80 3420
85 3450
90 3465
95 3570
100 3690-3700
Creo que la gran variacion que hay a pequeños dutys es por la resistencia shunt, ya que se hace mas influyente al llegar menos tension al motor, por lo que la caida de la R es mas apreciable y daterminante!!!
Saca tus conclusiones!!!
Saludo!!!!
-
Tienes por ahí la tabla de velocidades (teórica --- medida) poniendo en el programa el 3700?
-
Intenté hacerlo con una banda proporcional y porcentajes pero desistí.
Mira lo que he pensado:
Imaginemos que mandamos VRS232=3000r.p.m. el programa aplica la fórmula y le sale un determinado Duty, este Duty es aplicado al motor que a su vez mediante el opto envia pulsos. El programa calcula la nueva velocidad y le sale 3450r.p.m. (según tu tabla) por lo tanto hemos de reducir el Duty para que vaya más despacio.
El nuevo Duty sería el inicial menos el Duty resultante de la resta de las 2 velocidades, dando lugar a la siguiente fórmula: Duty corregido=((2·VRS232 - Vpulsos)·100)/3700
Aplica este Duty al motor, que te recalcule la velocidad y que la envie al hiperterminal, a ver si ahora salen bastante más iguales. Creo que sí.
Un saludo
-
Has pensado lo mismo que yo, pero kon una variante!!
Como tiene que ser un control proporcional ahi que multiplicar por una constante!!!
introducimos velocidad deseada; entonces el error, sera la velocidad deseada menos velocidad medida:
error=abs(recibido-velocidad);
se multiplica por una constante;
errorKP=error*KP;
y se genera el duty correspondiente a ese valor!! Si p.e. introducimos 2000rpm --> error = 2000 --> errorKP=2000*0.8; tomamos KP=0.8 p.e.! errorKP=1600 generamos duty -->43; y esto se lo restamos al duty inicial, que en este caso es 0
errorKP=error*KP;
duty=abs(duty-(errorKP*100.0)/3700.0);
set_pwm1_duty(duty);
Entonces medimos la velocidad obtenida 2600 mas o menos!!
Ahora el error es de 600
entonces errorKP=600*0.8= 480
calculamos duty de este que es 480*100/3700=12
se lo restamos al anterior
43 - 12 =31 y medimos la velocidad que es 1600;
error 400 por debajo; errorKP=400*0.8 =320; generamos duty que es 320 *100/3700 =8; entonces ahora viene el problema habria uqe sumarselo al anterior; que seria 31+8=39 y mediriamos la velocidad que es 2300; error 300 por encima, mismas operaciones de antes; errorKP=240; duty generado -->6, se lo restamos al anterior
39-6=33 y nos daria una velocidad de 1800, error 200; errorKP=160; duty generado 4; se lo sumamos al anterior
33+4=37, y medimos la velocidad de 2150; error 150; errorKP=120; duty generado 3, se lo restamnos al anterior; 37-3=34; velcocidad medida 2000, al final hemos conseguido que se nos acerque bastante, esto es el control proporcional!!
Todo esto en muy poco tiempo claro; un segundo o dos!!
Que te parece, creo que deberia ser asi; pero el problema esta en hacer el codigo dentro de mi programa!!
Si no entiendes algo dime!!
Un saludo
-
Se entiende perfectamente.
Es lo mismo que yo había pensado sin fórmulas de control P, con las diferencias:
- Yo he supuesto una Kp=1
- Como en la tabla de velocidades la Vpulsos medida es siempre mayor que la VRS232 teórica no he tenido en cuenta el otro intervalo (el que hay que sumar Dutys) ... pero tienes razón el desarrollo completo es teniendo en cuenta los 2 intervalos.
La programación a simple vista no parece que vaya a ser complicada. Mira a ver que te parece:
Velocidad teórica enviada vía RS232 = VRS232
Velocidad medida a partir de los pulsos = Vpulsos
Si VRS232 - Vpulsos > 0 ---> Sumar Duty al anterior
Si VRS232 - Vpulsos < 0 ---> Restar Duty al anterior
Si VRS232 - Vpulsos = 0 ---> Fin
-
Haber que te parece esto!!! Te `pomgo el codigo completo:
*****************************************************
#include <16f873.h>
#device adc=10
#include <stdlib.h>
#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8)
#zero_ram
float KP;
float errorKP;
int opcion, lazofinalizado;
int velocidadmedida;
int duty, dutygenerado, dutycorregido;
int16 error;
int16 pulsos;
int16 interrupcion;
int16 velocidad;
int16 recibido;
float dato;
char string[8];
int habilitacionVelo, habilitacionDuty;
int32 tiempoReal;
int32 Tiempo;
lazo_proporcional(){
if(recibido - velocidad > 0)
{
error=recibido-velocidad;
errorKP=error*KP;
dutygenerado=(errorKP*100.0)/3700.0;
dutycorregido=duty+dutygenerado;
set_pwm1_duty(dutycorregido);
duty=dutycorregido;
dutycorregido=0;
}
if(recibido - velocidad < 0)
{
error=velocidad-recibido;
errorKP=error*KP;
dutygenerado=(errorKP*100.0)/3700.0;
dutycorregido=abs(duty-dutygenerado);
set_pwm1_duty(dutycorregido);
duty=dutycorregido;
dutycorregido=0;
}
if(recibido - velocidad == 0)
{
lazofinalizado=1;
}
}
Contr_Velocidad(){
habilitacionDuty=0;
gets(string);
recibido=atol(string);
if(recibido==5000 || recibido==5001)
{
reset_cpu();
}
else
{
if(recibido>=800 && recibido<=3700)
{
habilitacionVelo=1;
}
else
{
set_pwm1_duty(duty);
}
}
}
Contr_duty_cycle(){
habilitacionVelo=0;
gets(string); //recibo la cadena
recibido=atol(string); //la convierto la cadena a entero
if(recibido==5000 || recibido==5001) //si se envia cualquiera de estos dos numeros se resetea el PIC, reset por software
{
reset_cpu();
}
else
{
if(recibido>=20 && recibido<=100)
{
duty=recibido;
set_pwm1_duty(duty); //El caracter ascii recibido pasado a entero
//establecido como ciclo de trabajo
habilitacionDuty=1; //flag pata habilitar el contaje de los pulsos de velocidad del optoacoplador
}
else //si no se ha recibido reset o desconexion, se prosigue a realizar el duty
{
set_pwm1_duty(duty);
}
}
}
#int_RTCC //se determina la funcion de la interrupcion
RTCC_isr()
{
if(habilitacionVelo==1 || habilitacionDuty==1)
{
if(habilitacionVelo==1 && velocidadmedida==1 && lazofinalizado==0)
{
lazo_proporcional();
}
tiempoReal++; //se suma a una variable que computa el tiempo transcurrido
pulsos=get_timer1(); //se obtiene el valor del contador timer1
interrupcion++; //se suma en uno el flag de interrupcion
if(interrupcion==100) //cuando se llega a 100, es decir, 1 segundo, 100*10ms=1000ms=1seg; se introduce en la funcion
{
velocidad=pulsos*15; //sacamos la velocidad con una regla de tres
velocidadmedida=1;
printf("%lu ",velocidad); //tambien puerto serie
printf("%u ",duty); //imprimo el duty para saber cual nos ha generado
Tiempo=tiempoReal*0.01; //calculo el tiempo en segundos transcurridos
printf("%lu ", Tiempo); //imprimo el tiempo
interrupcion=0; //se inicializa de nuevo el flag de interrupcion para que vuelva a contar 100 interrupciones
set_timer1(0); //se inicializa tambien el contador
set_rtcc(178); //se introduce de nuevo el valor del temporizador de interrupcion
}
else
{
set_rtcc(178);
set_timer1(pulsos);
}
}
}
main()
{
enable_interrupts(GLOBAL); //se habilitan interrupcion general
enable_interrupts(INT_RTCC); //habilita int. del timer0
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128); //se configura el timer0 como temporizador y una báscula de 128
set_rtcc(178); //10ms -> 10000us -> 10000/128=78 -> 255-78=177 se le inserta el tiempo a contar, se suma uno
duty = 0; //se inicializa el duty en cero
setup_ccp1(CCP_PWM); //Se habilita el pwm 1
setup_timer_2(T2_DIV_BY_16, 99, 1); //Se establece el valor de la frecuencia de 620Hz(1,6 ms el periodo) para el PWM
set_pwm1_duty(duty); //se manda el duty al PWM
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1); //se configura el timer1 como contador, de pulsos externos y de báscula 1
set_timer1(0); //se inicializa el contador en cero
interrupcion=0; //se inicializan los flags
habilitacionVelo=0, habilitacionDuty=0;
tiempoReal=0;
Tiempo=0;
dutygenerado=0;
dutycorregido=0;
error=0;
errorKP=0;
velocidadmedida=0;
lazofinalizado=0;
/******************************************* Constante KP************************************************/
KP=1; //la constante del control proporcional
/********************************************************************************************************/
while(true)
{
//el programa principal que estaria haciendo, esperando a la interrupcion del timer0
gets(string); //Guarda el Duty (0-255) en la variable string aunque solo necesitamos hasta 100
//y espera un RETURN (Ctrl 13)
opcion=atoi(string); //Usa la función ATOI para pasarlo a entero
switch (opcion) { //case para entrar en un codigo u otro
case 1: //recibido duty cycle se controla mediante duty cycle con un rango
while(true)
{
Contr_duty_cycle();
}
break;
case 2: //recibido velocidad se controla mediante velocidad con un rango
while(true)
{
Contr_Velocidad();
}
break; } //fin del "CASE"
}
}
-
Te lo he estado mirando y corrigiendo. Espero no haberme equivocado con los razonamientos.
- El emisor tendrá que estar enterado de estos rangos:
20<=dutys<=100 ; 800<=v<=3700 con un mensajito enviado desde el PIC
- En la función Contr_duty_cycle() sustituir el else por un else if(recibido>=20 && recibido<=100) y eliminar el último else (creo que no sirve para nada)
- El comentario gets(string); //Guarda el Duty (0-255) en la variable string aunque solo necesitamos hasta 100 yo lo corregiría puesto que no se espera un Duty si no un 1 o un 2.
- Idem para el comentario del case1 puesto que todavía no se ha recibido el duty.
- Si nada má conectar la alimentación al PIC eliges el case2 (velocidad), el if(habilitacionVelo==1 && velocidadmedida==1 && lazofinalizado==0) no lo va a ver ya que velocidadmedida=0 con lo cual no te entrará en el lazo proporcional. Claro si antes has hecho el case1 entonces sí que te entrará ya que ahora velocidadmedida=1.
- Poner dutycorregido=0 en el lazoproporcional() creo que no es necesario, funcionará igual.
- Antes de meterte en el lazoproporcional() tienes que calcular el duty por primera vez (duty=(recibido*100.0)/3700.0
de lo contrario se meterá con duty=0. Este cálculo mételo en la función Contr_Velocidad()
- El if(recibido - velocidad == 0) convendría quitarlo por si hay alguna desviación de la velocidad que se autocorrija a sí mismo.
Xa2 piensa en todos estos razonamientos y ya me dirás que te parecen.
Un saludo
-
Aqui te dejo el codigo, durante este fin de semana ya habia hecho cambios que me has puesto aqui, de todas formas gracias; he hecho mas cambios, aunque no todos los que me has puesto porque sino no funciona correctamente; es decir, como se desea!!
Gracias de nuevo por tu interes y ayuda!!!!
Haber si se puede pulir aun mas!!
*****************************************************
#include <16f873.h>
#device adc=10
#include <stdlib.h>
#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8)
#zero_ram
float KP;
float errorKP;
int opcion;
int duty, dutygenerado, dutycorregido;
int16 error;
int16 pulsos;
int16 interrupcion;
int16 velocidad;
int16 recibido;
float dato;
char string[8];
int habilitacionVelo, habilitacionDuty, FueraRango;
int32 tiempoReal;
int32 Tiempo;
lazo_proporcional(){
if(recibido > velocidad)
{
error=recibido-velocidad;
errorKP=error*KP;
dutygenerado=(errorKP*100.0)/3700.0;
dutycorregido=duty+dutygenerado;
if(dutycorregido>100)
{
set_pwm1_duty(100);
duty=100;
dutygenerado=0;
}
if(dutycorregido<20)
{
set_pwm1_duty(20);
duty=20;
dutygenerado=0;
}
if(dutycorregido<=100 && dutycorregido>=20)
{
set_pwm1_duty(dutycorregido);
duty=dutycorregido;
dutycorregido=0;
dutygenerado=0;
}
dutycorregido=0;
}
if(recibido < velocidad)
{
error=velocidad-recibido;
errorKP=error*KP;
dutygenerado=(errorKP*100.0)/3700.0;
dutycorregido=abs(duty-dutygenerado);
if(dutycorregido>100)
{
set_pwm1_duty(100);
duty=100;
dutygenerado=0;
}
if(dutycorregido<20)
{
set_pwm1_duty(20);
duty=20;
dutygenerado=0;
}
if(dutycorregido<=100 && dutycorregido>=20)
{
set_pwm1_duty(dutycorregido);
duty=dutycorregido;
dutygenerado=0;
}
dutycorregido=0;
}
}
Contr_Velocidad(){
habilitacionDuty=0;
gets(string);
recibido=atol(string);
if(recibido==5000 || recibido==5001)
{
reset_cpu();
}
else
{
if(recibido>=800 && recibido<=3700)
{
habilitacionVelo=1;
FueraRango=0;
}
else
{
printf("Rango 800-3700 r.p.m."
;
FueraRango=1;
}
}
}
Contr_duty_cycle(){
habilitacionVelo=0;
gets(string); //recibo la cadena
recibido=atol(string); //la convierto la cadena a entero
if(recibido==5000 || recibido==5001) //si se envia cualquiera de estos dos numeros se resetea el PIC, reset por software
{
reset_cpu();
}
else
{
if(recibido>=20 && recibido<=100)
{
duty=recibido;
set_pwm1_duty(duty); //El caracter ascii recibido pasado a entero
//establecido como ciclo de trabajo
habilitacionDuty=1; //flag pata habilitar el contaje de los pulsos de velocidad del optoacoplador
}
else
{
printf("Rango 20-100 duty cycle"
;
}
}
}
#int_RTCC //se determina la funcion de la interrupcion
RTCC_isr()
{
if(habilitacionVelo==1 || habilitacionDuty==1)
{
tiempoReal++; //se suma a una variable que computa el tiempo transcurrido
pulsos=get_timer1(); //se obtiene el valor del contador timer1
interrupcion++; //se suma en uno el flag de interrupcion
if(interrupcion==100) //cuando se llega a 100, es decir, 1 segundo, 100*10ms=1000ms=1seg; se introduce en la funcion
{
velocidad=pulsos*15; //sacamos la velocidad con una regla de tres
if(habilitacionVelo==1 && FueraRango==0)
{
lazo_proporcional();
}
printf("%lu ",velocidad); //tambien puerto serie
printf("%u ",duty); //imprimo el duty para saber cual nos ha generado
Tiempo=tiempoReal*0.01; //calculo el tiempo en segundos trnascurrido
printf("%lu ", Tiempo); //imprimo el tiempo
interrupcion=0; //se inicializa de nuevo el flag de interrupcion para que vuelva a contar 100 interrupciones
set_timer1(0); //se inicializa tambien el contador
set_rtcc(178); //se introduce de nuevo el valor del temporizador de interrupcion
}
else
{
set_rtcc(178);
set_timer1(pulsos);
}
}
}
main()
{
enable_interrupts(GLOBAL); //se habilitan interrupcion general
enable_interrupts(INT_RTCC); //habilita int. del timer0
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128); //se configura el timer0 como temporizador y una báscula de 128
set_rtcc(178); //10ms -> 10000us -> 10000/128=78 -> 255-78=177 se le inserta el tiempo a contar, se suma uno
duty = 0; //se inicializa el duty en cero
setup_ccp1(CCP_PWM); //Se habilita el pwm 1
setup_timer_2(T2_DIV_BY_16, 99, 1); //Se establece el valor de la frecuencia de 620Hz(1,6 ms el periodo) para el PWM
set_pwm1_duty(duty); //se manda el duty al PWM
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1); //se configura el timer1 como contador, de pulsos externos y de báscula 1
set_timer1(0); //se inicializa el contador en cero
interrupcion=0; //se inicializan los flags
habilitacionVelo=0, habilitacionDuty=0;
tiempoReal=0;
Tiempo=0;
dutygenerado=0;
dutycorregido=0;
error=0;
errorKP=0;
FueraRango=0;
/******************************************* Constante KP************************************************/
KP=1; //la constante del control proporcional
/********************************************************************************************************/
while(true)
{
//el programa principal que estaria haciendo, esperando a la interrupcion del timer0
gets(string);
opcion=atoi(string); //Usa la función ATOI para pasarlo a entero
switch (opcion) { //case para entrar en un codigo u otro
case 1: //recibido duty cycle se controla mediante duty cycle con un rango
while(true)
{
Contr_duty_cycle();
}
break;
case 2: //recibido velocidad se controla mediante velocidad con un rango
while(true)
{
Contr_Velocidad();
}
break;
default:
reset_cpu();
} //fin del "CASE"
}
}
-
Uf me duelen las pestañas de mirarlo y remirarlo.
Bien, ahora está bastante mejor, aunque creo que seguirá sin funcionarte bien por una de las cosillas que te comentaba anteriormente:
- Antes de meterte en el lazoproporcional() tienes que calcular el duty por primera vez (duty=(recibido*100.0)/3700.0 de lo contrario se meterá con duty=0. y entonces el primer dutygenerado será el de la resta.
Otra cosa que me ha llevado de cabeza: ¡¡¡Para que la función abs() funcione bien las variables de dentro tienen que ser signed!!!
-
Ya lo siento Pocher, pero muchas gracias!!
En cuanto a lo d meter priemro el duty antes de entrar al lazo proporcional, no lo hago porque el primer duty que deseo que se haga es el de la resta, ya que es un control cerrado proporcional!!!
Entonces se resta la velocidad medida menos la velocidad deseada, y la resta sera la que genere el priemr duty!!!Luego ya sera el error entre medidas y deseadas!!
En cuanto al Abs() me lo hace bien, aunque seria bueno cambiarlo por si acaso, no lo hago porque igual hay que cambiar msa cosas de programacion, y asi no me da ningun tipo de problema!!
Gracias de nuevo Pocher y saludos!!
Lo he probado y funciona a las mil maravillas!!!
-
Perdona si soy pesado pero creo que cuando la resta sea negativa la función abs() no va a ir bien. Yo lo comprobé.
Lo único que tienes que cambiar: int duty, dutygenerado, dutycorregido por signed int duty, dutygenerado, dutycorregido.
Un saludo.
-
Uf ya sé porque la función abs() "te funciona bien" sin tener declaradas como signed las variables que hay dentro de ella ... simplemente no está funcionando. Es decir si no declaras las variables que hay dentro de ella como signed no opera.
Traduciendo, si la quitas no va a pasar nada.
Prueba y cuentas.
-
Gracias Pocher!!!
Entonces dices que con declarar las variables como signed vale??
Entonces dejo la funcion abs() y declaro las variables como signed, no??
Haciendo esto funcionaria bien, no??
Gracias de nuevo por las molestias y un saludo!!
-
No mejor que eso, quítala y deja tan solo la resta, si mis cálculos no fallan esa resta nunca va a dar resultado negativo.
Prueba y cuentas.
-
:-/ :-/ :-/ holaaaa.....soy nuevo en el foro y quiero aprender con uds. ya q soy nuevo en el tema de pic..........me gustaria probar ese programilla q han realizado....se q debe haber costado mucho su realizacion pero quiero hacerlo.......esta funcionando correctamente???.........me pueden ayudar a atilizarlo o explicarme como funciona......ya les agradeceria q me dejaran compartir sus ideas.....adios.- :-/ :-/ :-/