martes, 20 de noviembre de 2007

Programa de control del puerto serie / motores

Ya tengo la placa que gestiona los motores soldada. Todo menos el puente en H (L293), con lo que por ahora tengo las salidas del microcontrolador (pic 16f876) enganchadas a unos leds, con resistencias de 220 ya que la salida es de 5v. Con esto consigo ver el funcionamiento del pic y el pueroto serie de una forma gráfica.

En cuanto pueda subiré unas fotos de como va la placa que gestiona los motores y si me da tiempo algun video. Por ahora os voy a comentar el programa en c que he creado en el PICC para recibir datos desde el puerto serie (rs232) y transformarlo en señales para los motores.

Lo primero que queria comentar es que el codigo me he basado en uno que hay hecho por Internet. Y he ido modificando cosas para poder aplicarlo a mi caso (que ha sido bastantes modificaciones).

Basicamente hay una funcion principal que activa los dos pwm(RC1 y RC2):

setup_ccp1(CCP_PWM); //ccp1 modo PWM
setup_ccp2(CCP_PWM); //ccp2 modo PWM

Inicializamos el TIMER2 y activamos dos interrupciones, las interrupciones globales y la de RDA que es cuando recibe datos por el puerto serie.

Despues el programa se queda en un bucle infinito esperando que hayan datos que procesar.

La interrupcion de rda (serial_isr();) se activa cuando llega algo por el puerto serie, conforme va recibiendo datos los va acumulando en un buffer para despues procesarlos.

Por funciones tenemos:

Inicializa --> que inicializa todas las variables globales de nuestro programa.

inicbuffer --> inicializa el buffer de caracteres leidos y lo pone todo a 0x00. Indispensable limpiarlo despues de usarlo, ya que se puede quedar basura que haga un funcionamiento anomalo.

addcbuffer --> Va añadiendo caracteres al buffer, en el caso de que un caracter sea un intro (0x0D) o un espacio (0x20) se corta la ejecucion para procesar los comandos.

aentero --> Convierte la cifra obtenida en caracteres a numero entero, aqui tambien vemos si el valor es negativo, en dicho caso el motor irá en sentido contrario.

procesa_comando --> Una vez que se han introducido los comandos esta funcion es la encargada de sacar los valores (iluminar leds) correspondientes que más adelante moverán los motores.

La forma de usar el programa es pasando valores por el puerto serie de la siguiente forma:
valor1 (espacio) valor2 (intro)

Por ejemplo para mover los motores hacia adelante de una forma continua podria ser:
50 50

Para mover un motor hacia adelante despacio y otro hacia atras muy rapido sería:
8 -100


A continuacion os pego el codigo entero del pic:



//-----------------------------------------------------------------

#include <16f876a.h> // Definiciones del PIC 16F876A
#include <
stdlib> //stdlib.h para el atoi
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Los Fuses de siempre
#use delay(clock=4000000) // Oscilador a 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// RS232 Estándar



// CONSTANTES /////////////////////////////////////////////////////////////////

int const lenbuff=5; // Longitud de buffer, Ajustar
// a lo que desees (o te sea posible)


// VARIABLES EN RAM ///////////////////////////////////////////////////////////

int motder,motizq;
int sentido, sentidoder, sentidoizq;
int xbuff=0x00; // Índice: siguiente char en cbuff
char cbuff[lenbuff]; // Buffer
char rcvchar=0x00; // último carácter recibido
int1 flagcommand=0; // Flag para indicar comando disponible
char arg[lenbuff]; // para pasar el negativo



// Declaración de Funciones ///////////////////////////////////////////////////

void inicializa(void);
void inicbuff(void); // Borra buffer
int addcbuff(char c); // añade carácter recibido al buffer
void procesa_comando(void); // Procesa comando
int aentero(void); // Pasa el buffer a un entero



// INTERRUPCIONES /////////////////////////////////////////////////////////////

#int_rda
void serial_isr() { // Interrupción recepción serie USART

rcvchar=0x00; // Inicializo carácter recibido
if(kbhit())
{ // Si hay algo pendiente de recibir ...
rcvchar=getc(); // lo descargo y ...
addcbuff(rcvchar); // lo añado al buffer y ...
printf("Recibo: %c ",rcvchar);
}
}



// Desarrollo de Funciones ////////////////////////////////////////////////////


void inicializa()
{
motder=motizq=0;
sentido=sentidoder=sentidoizq=0;
xbuff=0x00; // Índice: siguiente char en cbuff
inicbuff(); //char cbuff[lenbuff]; // Buffer
rcvchar=0x00; // último carácter recibido
flagcommand=0; // Flag para indicar comando disponible
}


void inicbuff(void) // Inicia a \0 cbuff -------------------
{
int i;

for(i=0;i Habilita Flag para procesar
flagcommand=1; // Comando en Main
motizq=aentero(); // Paso a entero
sentidoizq=sentido;// Cambio sentido
inicbuff(); // Borra buffer
break;

case 0x20: // Espacio
motder=aentero(); // Paso a entero
sentidoder=sentido;// Cambio sentido
inicbuff(); // Borra buffer
break;

default:
cbuff[xbuff++]=c; // Añade carácter recibido al Buffer
}
}


int aentero () //pasa el buffer a un entero
{

int retorno,i;


if(cbuff[0]=='-')
{
sentido=0;

i=1;

do{ // Extraemos argumento del buffer
arg[i-1]=cbuff[i]; // copiamos a arg sin el '-'
}while(cbuff[++i ]!=0x00);

retorno=atoi(arg);
}
else
{
sentido=1;
retorno=atoi(cbuff);
}
printf("A entero: %d\n",retorno);

return retorno;
}

// Programa Principal /////////////////////////////////////////////////////////

void main()
{

setup_ccp1(CCP_PWM); //ccp1 modo PWM
setup_ccp2(CCP_PWM); //ccp2 modo PWM

setup_timer_2(T2_DIV_BY_16, 127, 1); // 488Hz (con XT=4MHz) esta frecuencia la podes variar cambiando los valores, fijate en el datasheet del micro que uses, que vienen las relaciones para calcular lo inherente a la frecuencia y al duty.
inicbuff(); // Borra buffer al inicio

printf("Soy el Pic y estoy activo\n");
enable_interrupts(int_rda); // Habilita Interrupción RDA
enable_interrupts(global); // Habilita interrupciones

do {

if(flagcommand) procesa_comando(); // Si hay comando pendiente
// de procesar ... lo procesa.

} while (TRUE);

}

// Procesador de Comandos /////////////////////////////////////////////////////

void procesa_comando(void)
{

flagcommand=0; // Desactivo flag de comando pendiente.

printf ("procesa comando: %d\n",motder);
printf ("procesa comando: %d\n",motizq);

if(sentidoder!=0)
{
printf ("Enciende DER\n");
output_high(PIN_C3);
}
else
{
printf ("Apaga DER\n");
output_low(PIN_C3);
}

set_pwm1_duty(motder); // Se cambia el ancho con el valor obtenido en canal 0.

if(sentidoizq!=0)
{
printf ("Enciende IZQ\n");
output_high(PIN_C4);
output_high(PIN_C5);
}
else
{
printf ("Apaga IZQ\n");
output_low(PIN_C4);
output_low(PIN_C5);
}

set_pwm2_duty(motizq); // Se cambia el ancho con el valor obtenido en canal 0.

inicializa(); // Borra buffer
}

--------------------------------

Espero que os sea de ayuda.
UN SALUDO