Autor Tema: control motor DC y servo por RS232  (Leído 11109 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado ahona

  • PIC10
  • *
  • Mensajes: 7
control motor DC y servo por RS232
« en: 13 de Marzo de 2008, 01:49:31 »
Hola!!!
Llevo unos dias dandolo vueltas y la verdad es que no saco nada en claro...
Lo que quiero hacer es controlar el sentido de giro de un motor DC y un servo con un PIC 16F877 desde el PC.
Lo estoy simulando en proteus, pero creo que hago algo mal con el terminal porque no me hace ni caso, aunque la culpa la tendra el codigo que esta mal (me decanto mas por esta opcion).
Os pongo lo que llevo hasta ahora, que seguro que le faltan un monton de cosas, pero haber si a base de fallos aprendo...la verdad es que apenas llevo unos meses en esto.
Código: [Seleccionar]
#include <16f877.h>
#use delay (clock=4000000)   //indico la frecuencia del reloj

#USE RS232 (BAUD=9600,XMIT=pin_C6,RCV=pin_C7)

char direccion=0;

void main (void)
{
   setup_ccp1(CCP_PWM);   // Configura CCP1 en modo PWM para el motor
   setup_ccp2(CCP_PWM);   //configura el CCP2 en modo PWM para el servo
   set_tris_c(11111001);   //rc2 y rc1 como salida
   setup_timer_2(T2_DIV_BY_16,255,1);  // El periodo de la señal PWM
   set_tris_a(11111100); //RA0 y RA1 como salida de datos
   
   while(true)
   {
      direccion=getc(); //recojo en direccion el byte recibido
     
      while (direccion==1) //aqui muevo el motor en una direccion
      {
      output_low(pin_A1);//pin A1 a 0
      output_high(pin_A0);//pin A0 a 1
      set_pwm1_duty(50); //indico el ciclo de trabajo del pwm
      direccion=getc();
      }
     
      while (direccion==2) //aqui muevo el motor en el otro sentido
      {
      output_low(pin_A0);//pin A0 a 0
      output_high(pin_A1);//pin A1 a 1
      set_pwm1_duty(50);
      direccion=getc();
      }
     
      //--------------servo----------------------
      //esta supongo que seria la estructura que deberia tener,
      //los valores no estan calculados, solo estan puestos para
      //rellenar
      //-----------------------------------------
     
      while (direccion==3)     //esto se encarga del servo
      {                        //La idea es que, por ejemplo
      set_pwm2_duty(100);      //con esta funcion fije la posicion
      direccion=getc();        //central del servo y se mantenga ahi
      }                        //hasta que una de las siguientes acciones
                               //lo hagan variar y al finalizar, que
                               //regrese a esta posicion.

      while (direccion==4) //aqui el servo giraria a un lado
      {
      set_pwm2_duty(100);
      direccion=getc();
      }
     
      while (direccion==5) //aqui giraria al otro lado
      {
      set_pwm2_duty(100);
      direccion=getc();
      }
   }
}

Os adjunto el circuito en proteus, que ese si que creo que esta bien diseñado junto con el fichero .hex del programa...

Muchas gracias por vuestro tiempo

Un saludo!

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: control motor DC y servo por RS232
« Respuesta #1 en: 13 de Marzo de 2008, 04:03:45 »
Prueba con éste código:

Código: [Seleccionar]
#include <16f877.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,NOBROWNOUT,NOCPD,NOWRT
#use delay (clock=4000000)   //indico la frecuencia del reloj

#USE RS232 (BAUD=9600,XMIT=pin_C6,RCV=pin_C7)

#use fast_io(a)
#use fast_io(c)

char comando=0;

void main (void)
{
   set_tris_c(0b10000000);   //rc2 y rc1 como salida, rc7=RX entrada
   setup_timer_2(T2_DIV_BY_16,255,1);  // El periodo de la señal PWM
   set_tris_a(0b00000000); //RA0 y RA1 como salida de datos
   // PR2=255, TMR2 prescale value = 16, PWM period=1/(1,22Khz), Clock=4Mhz
   //
   // PWM period=0,82ms
   //
   // PWM duty cycle=X*1/(4000000)*16
   //
   // Ejm: Queremos un 40% de ciclo útil
   //
   // 0,4*0,82ms=X*16/4000000
   //
   // X=0,4*0,82ms*4000000/16 => X=82
   
   while(true)
   {
     
      while (!kbhit());// Mientras no haya datos para recibir me quedo
                       // Aqui esperando
      comando=getc();
     
      switch (comando){
         case '1':
            output_low(pin_A1);// Gira en un sentido
            output_high(pin_A0);
         break;
         
         case '2':
            output_high(pin_A1);// Gira en el otro
            output_low(pin_A0);
         break;
         
         case '3':
            output_low(pin_A1);// Detiene
            output_low(pin_A0);
         break;
         
         case '4':
            set_pwm1_duty(82);// 40% de ciclo útil
            set_pwm2_duty(205);// 100% de ciclo útil
         break;
         
         case '5':
            set_pwm1_duty(123);// 60% de ciclo útil
            set_pwm2_duty(164);// 80% de ciclo útil
         break;
         
         case '6':
            set_pwm1_duty(164);// 80% de ciclo útil
            set_pwm2_duty(123);// 60% de ciclo útil
         break;
         
         case '7':
            set_pwm1_duty(205);// 100% de ciclo útil
            set_pwm2_duty(82);// 40% de ciclo útil
         break;
         
         case '8':
            setup_ccp1(CCP_PWM);// Encendemos el módulo PWM
            setup_ccp2(CCP_PWM);
         break;
         
         case '9':
            setup_ccp1(CCP_OFF);// Lo apagamos
            setup_ccp2(CCP_OFF);
         break;
      };
   }
}

Adjunto carpeta comprimida con el circuito que monté en proteus, pero en físico tienes que investigar mejor de como conectar todo, en el proteus es una simulación y me permito conectar los motores directamente al pic, ojo, también se encuentra el .hex y el .c

Espero que esto te sirva de guía.

Adicionalmente me podrías explicar de forma sencilla como funciona un servo?

Saludos.

Desconectado ahona

  • PIC10
  • *
  • Mensajes: 7
Re: control motor DC y servo por RS232
« Respuesta #2 en: 13 de Marzo de 2008, 10:23:43 »
Bueno, que decirte  :shock:
Te estoy tremendamente agradecido, pues basicamente es lo que yo andaba buscando.
En lo que me liaba es en la simulacion en proteus (aparte del codigo), pues yo lo tengo simulado de la manera fisica, que creo que esta correcto mas o menos, pero no de la manera correcta para el proteus...
El programa es una base muy, pero que muy buena y a partir de ahi seguire avanzando el proyectillo.
Lo unico, es que para mover el motor, de la forma que lo tienes conectado, no lo se seguro, pero igual le hace falta una alimentacion adicional. Lo digo porque no se la tension que ofrecen los pines A0 y A1, aunque con 4 voltios bastaria para mover el motor (aparte de que le falta algun diodo de proteccion y demas).
Realmente, el programa segun esta bastaria, pero yo buscaba, ademas de controlar la direccion de giro del motor, poder controlar tambien su velocidad. Como he dicho, llevo relativamente poco en esto, pero para controlar su velocidad, seria por medio del PWM, no??
Lo que me pasa ami, es que al añadirle al motor la señal PWM del micro, no vario la velocidad, sino que vario la aceleracion, es decir, la alimentacion le llega por medio de los pines A0 y A1 y con el PWM, dependiendo de su ciclo de trabajo, sera mayor o menor su aceleracion y la idea es conseguir una velocidad fija...
Esto aun no se como hacerlo, pero ya lo mirare, pues lo he intentado con el integrado L293B, pero como ves, no he tenido exito.

De nuevamente te doy las gracias por la explicacion que viene en el codigo de como calcular lo del setup_timer y el pwm_duty, pues aun tenia dudas de como se obtenia.

En cuanto a lo del funcionamiento de un servo...segun lo has programado creo que esta mas que bien.
Seguro que hay gente que sabe mas que yo, pero por lo que se, el servo es un motor asociado a un pequeño circuito que es el encargado de posicionar el servo en una posicion u otra.
Un servo normal (sin trucar) tiene un giro de 180º. Para controlar la posicion del servo, se hace mediante el ciclo de trabajo del PWM. Segun tengo entendido, el periodo total con el que trabaja un servo es de 10 a 22 milisegundos. Para poder posicionar el servo, variamos el ciclo de trabajo calculado en milisegundos.

En 1'5 milisegundos el servo estaria posicionado en su posicion central, por asi decirlo, en 90º.
En 0'3 milisegundos el servo se posicionaba en una de las posiciones extremas, es decir, en los 180º.
En 2'1 milisegundos el servo se posiciona en la otra posicion extrema, los 0º.

Variando el valor de los milisegundos conseguimos que el servo tome cualquier posicion entre 0 y 180º.

Espero no haberme enrollado demasiado y haberte podido ser de tanta ayuda como tu ami.

Segun vaya avanzando el programa, si consigo controlar la velocidad, lo ire posteando por aqui.

Muchisimas gracias por vuestra ayuda.

Un saludo!!

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: control motor DC y servo por RS232
« Respuesta #3 en: 13 de Marzo de 2008, 13:21:18 »
Me puse a leer un poco el tema de RedPic y gracias a tí por explicarme un poco el funcionamiento de un servo, ahora entiendo un poquito más, prueba con éste código, en él explico como calculé todo.

Código: [Seleccionar]
#include <16f877.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,NOBROWNOUT,NOCPD,NOWRT
#use delay (clock=4000000)   //indico la frecuencia del reloj

#USE RS232 (BAUD=9600,XMIT=pin_C6,RCV=pin_C7)

#use fast_io(a)
#use fast_io(c)

char comando=0;

void main (void)
{
   set_tris_c(0b10000000);   //rc2 y rc1 como salida, rc7=RX entrada
   setup_timer_2(T2_DIV_BY_16,250,5);  //
   set_tris_a(0b00000000); //RA0 y RA1 como salida de datos
   // PR2=250, TMR2 prescale value = 16, PWM period=1/(50hz), Clock=4Mhz
   //
   // Postscale del timer2 = 5
   //
   // PWM period=20ms
   //
   // La cuestión está en saber cómo? determinar esos valores de PR2 y Postscale
   //
   // Ver fórmula en el datasheet del pic, sección 8.3.1
   //
   // 20ms=[PR2+1]*4*(1/4000000)*16
   //
   // Despejando y resolviendo nos dá que PR2=1249
   //
   // Pero el máximo valor que podemos darle es 255
   //
   // Entonces dividimos por 255 y nos dá 4,9
   //
   // vamos a tomar 5, es decir tenemos que dividir por 5 todavía
   //
   // esto lo podemos lograr con el Postscale, asi que le asignamos ese valor
   //
   // Ahora bien, tenemos que volver a calcular el valor de PR2
   //
   // Queremos que PR2 valga 1249 con ayuda del Postscale
   //
   // Esto es 1249=5*PR2 => PR2=249,8 vamos asumir 250
   //
   // Si sustituimos 250*5=1250=PR2
   //
   // [PR2+1]*4*(1/4000000)*16=20,016ms// No sé si estos 0,016ms afecten
   //
   // el correcto funcionamiento del servo.
   //
   //
   // PWM duty cycle=X*5*(1/4000000)*16
   //
   // Ejm: Si queremos un 1.5ms de ciclo útil
   //
   // 1,5ms=X*5*16/4000000
   //
   // X=1,5ms*4000000/(16*5) => X=75 (Centro del recorrido del servo)
   //
   // X=2,5ms*4000000/80=125 (Retrocede desde el punto medio 90º)
   //
   // X=0,5ms*4000000/80=25 (Avanza desde el punto medio 90º)
   
   while(true)
   {
     
      while (!kbhit());// Mientras no haya datos para recibir me quedo
                       // Aqui esperando
      comando=getc();
     
      switch (comando){
         case '1':
            output_low(pin_A1);// Gira en un sentido
            output_high(pin_A0);
         break;
         
         case '2':
            output_high(pin_A1);// Gira en el otro
            output_low(pin_A0);
         break;
         
         case '3':
            output_low(pin_A1);// Detiene
            output_low(pin_A0);
         break;
         
         case '4':
            set_pwm1_duty(75);//
            set_pwm2_duty(205);//
         break;
         
         case '5':
            set_pwm1_duty(125);//
            set_pwm2_duty(164);//
         break;
         
         case '6':
            set_pwm1_duty(25);//
            set_pwm2_duty(123);//
         break;
         
         case '7':
            setup_ccp1(CCP_PWM);// Encendemos el módulo PWM
            setup_ccp2(CCP_PWM);
         break;
         
         case '8':
            setup_ccp1(CCP_OFF);// Lo apagamos
            setup_ccp2(CCP_OFF);
         break;
      };
   }
}

Yo para variar la velocidad de un motor dc e usado el LMD18200 que permite controlar la velocidad mediante PWM y tiene freno, y le cambias el sentido de giro facilmente, sé que hay otros integrados más pequeños que hacen lo mismo, será cuestión de que busques un poquito más o si alguién conoce uno, mejor.

Saludos.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: control motor DC y servo por RS232
« Respuesta #4 en: 13 de Marzo de 2008, 22:46:31 »
No podía quedarme tranquilo y me puse a jugar un poquito  :mrgreen: viendo que RedPic aumentaba o decrementaba la posición del servo mediante comandos, incrementando o decrementando el ciclo útil, busqué hacer lo mismo y lo logré (gracias RedPic) usando las teclas '4' y '6', decrementamos e incrementamos la posición del servo poco a poco y con el '5' lo ubicamos en la posición central, si pulsamos el '7' encendemos el módulo de PWM y con el '8' lo apagamos, además de que en el virtual terminal te dice el valor que se fija el duty, esta bien chevere.

Código: [Seleccionar]
#include <16f877.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,NOBROWNOUT,NOCPD,NOWRT
#use delay (clock=4000000)   //indico la frecuencia del reloj

#USE RS232 (BAUD=9600,XMIT=pin_C6,RCV=pin_C7)

#use fast_io(a)
#use fast_io(c)

#define minimo 62
#define medio  94
#define maximo 125

char comando=0;
int8 valor=medio;




void main (void)
{
   set_tris_c(0b10000000);   //rc2 y rc1 como salida, rc7=RX entrada
   setup_timer_2(T2_DIV_BY_16,250,5);  //
   set_tris_a(0b00000000); //RA0 y RA1 como salida de datos
   set_pwm1_duty(valor);// Para que al encender el módulo esté en el medio
   // PR2=250, TMR2 prescale value = 16, PWM period=1/(50hz), Clock=4Mhz
   //
   // Postscale del timer2 = 5
   //
   // PWM period=20ms
   //
   // La cuestión está en saber cómo? determinar esos valores de PR2 y Postscale
   //
   // Ver fórmula en el datasheet del pic, sección 8.3.1
   //
   // 20ms=[PR2+1]*4*(1/4000000)*16
   //
   // Despejando y resolviendo nos dá que PR2=1249
   //
   // Pero el máximo valor que podemos darle es 255
   //
   // Entonces dividimos por 255 y nos dá 4,9
   //
   // vamos a tomar 5, es decir tenemos que dividir por 5 todavía
   //
   // esto lo podemos lograr con el Postscale, asi que le asignamos ese valor
   //
   // Ahora bien, tenemos que volver a calcular el valor de PR2
   //
   // Queremos que PR2 valga 1249 con ayuda del Postscale
   //
   // Esto es 1249=5*PR2 => PR2=249,8 vamos asumir 250
   //
   // Si sustituimos 250*5=1250=PR2
   //
   // [PR2+1]*4*(1/4000000)*16=20,016ms// No sé si estos 0,016ms afecten
   //
   // el correcto funcionamiento del servo.
   //
   //
   // PWM duty cycle=X*5*(1/4000000)*16
   //
   // Ejm: Si queremos un 1.5ms de ciclo útil
   //
   // 1,5ms=X*5*16/4000000
   //
   // X=1,5ms*4000000/(16*5) => X=75 (Centro del recorrido del servo)
   //
   // X=2,5ms*4000000/80=125 (Retrocede desde el punto medio 90º)
   //
   // X=0,5ms*4000000/80=25 (Avanza desde el punto medio 90º)
   
   while(true)
   {
     
      while (!kbhit());// Mientras no haya datos para recibir me quedo
                       // Aqui esperando
      comando=getc();
     
      switch (comando){
         case '1':
            output_low(pin_A1);// Gira en un sentido
            output_high(pin_A0);
         break;
         
         case '2':
            output_high(pin_A1);// Gira en el otro
            output_low(pin_A0);
         break;
         
         case '3':
            output_low(pin_A1);// Detiene
            output_low(pin_A0);
         break;
         
         case '6':
            if (valor<maximo) valor++;
            printf("PWM duty cycle: %u\r",valor);
            set_pwm1_duty(valor);//
            set_pwm2_duty(164);//
         break;
         
         case '5':
            valor=medio;
            printf("PWM duty cycle: %u\r",valor);
            set_pwm1_duty(valor);//
         break;
         
         case '4':
            if (valor>minimo) valor--;
            printf("PWM duty cycle: %u\r",valor);
            set_pwm1_duty(valor);//
            set_pwm2_duty(123);//
         break;
         
         case '7':
            setup_ccp1(CCP_PWM);// Encendemos el módulo PWM
            setup_ccp2(CCP_PWM);
         break;
         
         case '8':
            setup_ccp1(CCP_OFF);// Lo apagamos
            setup_ccp2(CCP_OFF);
         break;
      };
   }
}

Pruebalo y lo comentas  :P

PD1: Los valores los determiné de forma empírica, mejor dicho jugando  ;-)

PD2: Adjunto carpeta con archivos

Saludos.
« Última modificación: 13 de Marzo de 2008, 22:55:29 por gu1llermo »

Desconectado ahona

  • PIC10
  • *
  • Mensajes: 7
Re: control motor DC y servo por RS232
« Respuesta #5 en: 14 de Marzo de 2008, 11:58:29 »
 :o
Mil gracias, lo he hechado un ojo y esta muy elegante, yo hoy me voy de vacacines y vuelvo dentro de una semana, asi que trateare un poco con ello y a la vuelta ya te comento como lo llevo, por que tambien quiero que el motor, unicamente gire mientras tenga pulsada la tecla, y que se pare al soltarla.

Un saludo y muchas gracias!!

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: control motor DC y servo por RS232
« Respuesta #6 en: 19 de Marzo de 2008, 02:42:55 »
Guillermo ¿los servos no trabajan entre T=10ms y T=20ms?

He estado simulando y el periodo que te sale es de T=4ms.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: control motor DC y servo por RS232
« Respuesta #7 en: 19 de Marzo de 2008, 17:00:30 »
Hola pocher nuevamente, bueno éste fué el primer programa que hice para controlar un servo y en ese momento no conocía el plug-in del mplab para medir los tiempo y con el proteus no entiendo bien como configurar el osciloscopio, voy a volver analizarlo y te comento.

Saludos y gracias nuevamente por tu observación.

// Editado posteriormente:

Ya ví el problema, está aquí en ésta fórmula:

PWM period= 20ms = [PR2+1]*4*(1/4000000)*16

yo cargué PR2 con 250 y si desarrollamos [250+1]*4*(1/4000000)*16= 0,004016=4,016ms
que es muy distinto a los 20ms que yo quiero, yo pensaba que con cargar el Postscale con 5 esto se multiplicaría y obtendríamos 20,08ms ya en éste punto no sé como obtener los 20ms pensé que así lo lograría, alguna sugerencia?
« Última modificación: 19 de Marzo de 2008, 17:22:39 por gu1llermo »

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: control motor DC y servo por RS232
« Respuesta #8 en: 20 de Marzo de 2008, 03:55:31 »
Hola Guillermo. La única solución que encontré en su día, fué modificar la fosc. He abierto un nuevo post donde se explica como. Es este: http://www.todopic.com.ar/foros/index.php?topic=20960.new#new

Un saludo

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: control motor DC y servo por RS232
« Respuesta #9 en: 20 de Marzo de 2008, 09:41:07 »
Gracias! pocher, en verdad muchas gracias, e aprendido algo nuevo hoy  :mrgreen:

Saludos.

Desconectado STICK_MASTER

  • PIC10
  • *
  • Mensajes: 30
Re: control motor DC y servo por RS232
« Respuesta #10 en: 20 de Marzo de 2008, 11:39:13 »
Hola

queria decirte que si quieres un perfecto control del servo debes generar un pwm que dure en total 15 milisegundos variando el ciclo util entre 500 micros para cero grados y 2.5 mili segundos para 180 grados, no te recomiendo el modulo pwm del micro

tambien es importante definir la paridad y el numero de bits que usaras men suerte con ese proyecto :-/
Lo importante no es de lo que dispones lo importante es como lo uses