Mostrando las entradas con la etiqueta Programación. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Programación. Mostrar todas las entradas

miércoles, julio 23, 2008

Paso 22: Envuelto para regalo

Nos aproximamos a la conclusión de algunas etapas del proyecto y, por ende, es menester ordenar las ideas, agrupar conceptos y rectificar esquemas incorrectos. De ésta forma, se definirán el sistema mecánico, como una de las tareas de mayor dificultad, y el desarrollo del software, principalmente la rutinas de ejecución del control.

Hasta hace unas semanas contábamos con una serie de funciones que trabajaban de manera individual, pero que resultaron útiles para verificar diversas aspectos como la comunicación serial, interfaz con la placa adquisidora, ejecución de los lazos de control, etc. Sin embargo, llegamos al estado en que todos estos bloques inconexos deben estar enlazados entre si para conformar el programa final y se facilite la tarea de puesta punto antes de la presentación.

Inicialmente, para los primeros ensayos, contábamos con algunas instrucciones que recibían parámetros de configuración y estado. A medida que el soft crecía en funcionalidad, se debían pasar un mayor número de parámetros o la repetida compilación del programa afectando un conjunto limitado de variables y macros. Ésta dificultad desencadenó la necesidad de emplear una herramienta más versátil y que nos permita armar una interfaz simple con el usuario para la configuración del sistema completo.

El camino nos llevó hasta ncurses y los resultados hasta el momento han sido satisfactorios. Hemos logrado interactuar de manera sencilla con la planta, modificando los parámetros deseados y ejecutando las rutinas principales del sistema.

Una captura de pantalla muestra el aspecto que tiene la interfaz de usuario:


Enfocados sobre los detalles del software, hemos establecido dos ámbitos sobre los que se fundaron todos los componentes empleados.

El primero, definido en el espacio del kernel y con el soporte de RTAI, se trata de un módulo cargable destinado a la ejecución de una tarea de tiempo real denominada Despachador, que Se implementó como una máquina de estados, encargada de establecer el modo de trabajo del sistema. La tarea recibe tres parámetros desde el proceso usuario por medio de un FIFO: Modo, Frecuencia y Comando; destinados al modo de trabajo, frecuencia del PWM y comando de operación, respectivamente.

En detalle podemos decir que la tarea de tiempo real realiza las siguientes operaciones:

- Bucle principal: selecciona la accion a realizar dependiendo del modo de trabajo seleccionado por el usuario.

* Modo Libre: Funciona a lazo abierto y sirve para determinar si comunicación y drivers funcionan correctamente.

* Modo Control: Es el modo principal donde queda definido el sistema de control.

* Modo Calibración: Realizar ajustes de cero y ganancia para posición lineal y angular de los sensores.

* Modo Espera: Queda a la espera de una nueva acción a ejecutar y depende del modo de trabajo seleccionado.

Además, cuenta con las siguientes rutinas:

- Comunicación: módulo que permite la comunicación half-duplex entre la PC y el uC, para todos los modos de trabajo.

- Actuación: Dependiente del motor y determina las referencias, ciclos de trabajo de los pwm y otros datos a determinar.

- Sensado: Destinado a la captura de los pulsos de los encoders e inclinómetro.



En segundo lugar, quedó definido en el espacio de usuario el proceso encargado de realizar la interfaz con el operador. Presenta un menu de configuracion, comandos y visualizacion de datos (posicion, angulo, velocidad, estado de la comunicación, etc). Inicialmente el usuario elige el modo de trabajo deseado (libre, controlador, calibrador), luego la frecuencia de los PWM de una lista de opciones que dispone el uC y finalmente, el menú de comandos. Éste último menú permite por un lado, mediante las teclas 4, 6 y 1 hacer avanzar, retroceder y deterner el puente, por el otro, muestra en pantalla la configuración de trabajo y los datos recibidos desde la planta.


viernes, junio 13, 2008

Paso 21: Ejemplos de aplicación 2º Parte - Atmega16

Otro de los aspectos que concierne al desarrollo de nuestro proyecto final de carrera, tiene que ver con el microcontrolador de Atmel, Atmega16.

Éste microcontrolador de 8 bits, presenta una serie de características que lo hacen especialmente funcional con nuestros requerimientos. La principal deficiencia que nos presenta el hardware disponible, es el escaso número de entradas y salidas discretas de la placa adquisidora para poder realizar el ciclo de control de manera integral en la PC.

Por éste motivo, el microcontrolador Atmega16 nos permite, por un lado, establecer la comunicación con la PC por medio de RS-232, y por el otro, realizar una interfaz apropiada para la salida de 2 canales PWM en un amplio rango de frecuencias. También, en caso de ser necesario, se encargará de detectar los pulsos de los 2 encoders montados sobre la estructura.

A continuación publicamos dos ejemplos de aplicación, realizados durante la fase de diseño para probar los módulos de salida PWM y de comunicación por medio de la USART.

El entorno de desarrollo elegido es WinAVR , el compilador GNU GCC y la librería de C para AVR avr-libc. Si bien los ejemplos están pensados para Atmega16, también pueden aplicarse a otros modelos de la familia, como Atmega32, Atmega8, etc. por medio de modificaciones menores.

PWM.c (con interrupciones)


#include <avr/io.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr\sfr_defs.h>

enum { UP, DOWN };

INTERRUPT (SIG_OVERFLOW0) /*SIG_OUTPUT_COMPARE0*/
{
static unsigned char direction;
static unsigned char pwm;

switch (direction)
{
case UP:
if (++pwm == 255)
direction = DOWN;
break;

case DOWN:
if (--pwm == 0)
direction = UP;
break;
}

OCR0 = pwm;
return (0);
}


int main (void)
{
/* Define PB3 como salida PWM (pin 4)*/
DDRB = (1 <<3);

TCCR0 = (0 << FOC0)|(1 << WGM00)|(1 << COM01)|(0 << COM00)|(1 << WGM01)|(1 << CS02)|(0 << CS01)|(0 << CS00);

/* valor de comparacion inicial */
OCR0 = 0;

TIMSK = (1 << TOIE0);
sei();

for (;;)

return (0);
}


RS232.c (Envía y recibe caracteres por polling)

#include <avr/io.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr\sfr_defs.h>

#define FOSC 1000000 // clock en Hz

/* prototipos de funcion */
void bucle (void);
void uart_enviar(unsigned char *, unsigned char);
unsigned char uart_recibir(void);

/* variables globales */
unsigned int cuenta;


int main (void)
{

UBRRH=0x00;
UBRRL=0x0C; //Baud rate = 4800
UCSRA=0x00;

UCSRB=0x18; //TXEN=1 y RXEN=1 (Habilitacion Transmision y Recepcion)

UCSRC=0x86; //1 bit Stop. 8 bits por Caracter

bucle();
}

void uart_enviar (unsigned char *dato, unsigned char cant_dato)
{
unsigned char i;

for(i = 0; i < cant_dato; i++)
{
UDR = *(dato+i);
while(!(UCSRA & (1<<TXC)));
UCSRA=0x40;
}

}


unsigned char uart_recibir(void)
{

while( !( UCSRA & ( 1 << RXC) ) );

return UDR;
}


void bucle(void)
{
unsigned char rx;
unsigned char *cadena = "Tx y Rx Test\n";

cuenta = 0;

while(1)
{
rx = uart_recibir();

uart_enviar(cadena, strlen(cadena) );
}
}

viernes, mayo 30, 2008

Paso 21: Ejemplos de aplicación - RTAI

Nos quedaba pendiente la publicación de algunos códigos como ejemplo de una aplicación RTAI. Si bien es una aproximación dado que aún estamos realizando diversas pruebas sobre el software y la electrónica, creemos que es una manera útil de ir mostrando los progresos del proyecto.

En éste caso, presentamos un ejemplo de una tarea de tiempo real, ejecutada como parte de un módulo del kernel, y un proceso usuario que muestra datos en pantalla. La tarea se encarga de muestrear cada 50 useg, las entradas discretas de la placa adquisidora. En el puerto, está conectado un encoder incremental de cuadratura y el algoritmo implementado determina si se debe incrementar o decrementar un pulso en el acumulador.

A continuación, se detallan los archivos empleados con algunos comentarios incluidos en el código.

Makefile

-------------------------------------------------------------------
obj-m := ktest_encoder.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
EXTRA_CFLAGS := -I/usr/realtime/include -I/usr/include/ -ffast-math -mhard-float

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
gcc -o utest_encoder utest_encoder.c
-------------------------------------------------------------------



.runinfo

-------------------------------------------------------------------
latency:ksched+fifos:push ktest_encoder;./utest_encoder;popall:control_c
-------------------------------------------------------------------


ktest_encoder.c

-------------------------------------------------------------------
/*
Archivo: ktest_encoder.c

COPYRIGHT (C) 2007-2008 Elias S. Fliger (elias.s.f@gmail.com)
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/
/*----------------------------------------------------------
Descripción:
Se ejecuta 1 tarea denominada ENCODER.
Lee los canales A y B (chA y chB) y almacena los valores actuales
y anteriores del encoder en los vectores A y B para incrementar
o decrementar la variable x que especifica la posicion angular del motor.
----------------------------------------------------------*/
/* includes*/
#include <linux>
#include <linux>
#include <asm.h>
#include <math.h>
#include <rtai.h>
#include <rtai_sched.h>
#include <rtai_fifos.h>
/* defines*/
#define TICK_PERIOD 50000 //50 useg de periodo
#define TASK_PRIORITY 0
#define STACK_SIZE 10000
#define FIFO 0

#define BASEPORT 0x300 //Dirección Base de la placa adquisidora

/* globals */
static RT_TASK rt_task3;

/********************************************************
* FUNCION: encoder
*
* Descripcion: Tarea de lectura del encoder.
*
* Esta rutina lee los canales IN0 e IN1 (pin31 y 30) como canales A y B, respectivamente.
* Compara el estado de cada canal cada 50 useg ya que no posee interrupciones y
* luego incrementa o decrementa el acumulador de pulsos.
*********************************************************/
static void encoder(int t)
{
unsigned char chA=0, chB=0, entrada=0; //canales A y B , mas la mascara del puerto de entradas discretas
unsigned int A[2] = {0,0}; //Vector para comparar el valor actual y anterior del canal A
unsigned int B[2] = {0,0}; //Idem
int x=0; //Acumulador de pulsos

entrada=inb( BASEPORT);
chA = entrada & 0x01; //Lee IN 0 - Pin 31
chB = entrada & 0x02; //Lee IN 1 - Pin 30
A[0] = ( unsigned int )chA; //Inicializa canales de entrada
B[0] = ( unsigned int )chB;

rtf_reset(FIFO);


while(1)
{

entrada=inb( BASEPORT);

chA = entrada & 0x01;
chB = entrada & 0x02;
chB = chB/2;

A[1] = ( unsigned int )chA;
B[1] = ( unsigned int )chB;

if((A[1] & B[1])) //Ejecutar solo si A[1] = B[1] = 1
{
if( ((A[1]^A[0]) | (B[1]^B[0])) ) //si hubo cambio de estado para A o B, Incr o Decr
{
if((A[1]^B[0])) x++; // Si A[1] xor B[0] = 1 incrementa
else
{
if(x>0) x--; // De otra forma decrementa
else x=0;
}
}
}

A[0]=A[1]; //Actualiza valores
B[0]=B[1];

rtf_put(FIFO, &x, sizeof(x)); //Coloca en el FIFO para Proceso usuario

rt_task_wait_period();
}

}

/********************************************************
* FUNCION: init_module
*
* Descripcion: Inicializa el modulo.
*
* Inicializa tarea de tiempo real, establece modo periódico,
* crea FIFO e inicia temporizador.
*********************************************************/
int init_module(void)
{
RTIME tick_period;

rt_set_periodic_mode();

rt_task_init(&rt_task3, encoder, 1, STACK_SIZE, TASK_PRIORITY, 0, 0);
//tarea, funcion,valor inicial, tamaño stack, prioridad, usa FPU, usa Signal Handler.

rtf_create(FIFO, 10);

tick_period = start_rt_timer(nano2count(TICK_PERIOD));

rt_task_make_periodic(&rt_task3, rt_get_time() + tick_period, tick_period);

return 0;
}

/********************************************************
* FUNCION: cleanup_module
*
* Descripcion: Libera el modulo.
*
* Detiene temporizador, destruye FIFO y elimina tarea de tiempo real.
*********************************************************/
void cleanup_module(void)
{
stop_rt_timer();

rt_busy_sleep(10000000);
//recomendado

rtf_destroy(FIFO);
rt_task_delete(&rt_task3);
return;
}

MODULE_AUTHOR("Elias S. Fliger, ");
MODULE_DESCRIPTION("Proyecto BORA - RT test encoder - UNQ");
MODULE_LICENSE("GPL");
-------------------------------------------------------------------




utest_encoder.c

-------------------------------------------------------------------
/*
Archivo: utest_encoder.c

COPYRIGHT (C) 2007-2008 Elias S. Fliger (elias.s.f@gmail.com)
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/

/*----------------------------------------------------------
Descripción:
El proceso solo recibe el valor del acumulador de pulsos
obtenido en el modulo ktest_encoder y lo muestra en pantalla.
Ejecutar ./utest_encoder
----------------------------------------------------------*/
/* includes*/
#include
#include
#include
#include
#include
#include
#include
#include

/* defines*/
#define BASEPORT 0x300
//direccion base de la placa de adquisicion

static int end;
static void endme(int dummy)
{
end=1;
}


int main(int argc, char * argv[])
{
int fifo;
int Contador;

if ((fifo = open("/dev/rtf0", O_RDONLY))
-------------------------------------------------------------------