Вы находитесь на странице: 1из 6

Un simple modulador de anchura de impulso con Linux/RTAI

Por Pramode C.E Traduccin al espaol por Fernando Perfumo el da 18 de Mayo de 2004, para La Gaceta de Linux En un earlier article, present los fundamentos de la programacin en tiempo real con Linux/RTAI. En el presente artculo se muestra un bonito truco que puede hacer con el ordenador de su casa suponiendo que no le importe conectar un sencillo circuito elctrico al puerto paralelo (y, claro est, que use un kernel parcheado RTAI ). Asimismo se muestra el uso elemental de : mensajes entre tareas buzones Sea cuidadoso al manipular el hardware del PC, y no venga a por m si quema algo!

El truco
Se pretende hacer que un LED se ilumine y que, lenta y gradualmente vaya adquiriendo un brillo mayor para luego ir apagndose poco a poco, repitiendo contnuamente este ciclo. La forma de controlar el brillo de un LED consiste en actuar sobre la corriente elctrica que fluye a su travs, pero el problema est en que el circuito del puerto paralelo del PC proporciona slo dos niveles de tensin : algo mas de 0 voltios para el nivel bajo y algo menos de 5 voltios para el nivel alto. Como no es posible cambiar esto, vemos que se nos permite unicamente conectar y desconectar el LED , de forma casi instantnea, lo cual no es en principio lo que se requiere.

Modulacin de anchura de impulso


Imagine que va en bicicleta por una carretera pero pedaleando de un modo muy particular: con fuerza durante 3 segundos y dejndose llevar durante otros 7 segundos. Si contina as durante algn tiempo, recorrer una determinada distancia entre dos puntos. Dividiendo el espacio recorrido por el tiempo empleado se obtiene una velocidad media. Qu pasa con dicha velocidad media si se incrementa la proporcin del tiempo en que se pedalea? Crecer con total seguridad, y decrecer en caso contrario. De un modo similar, en lugar de aplicar un voltaje constante al LED aplicaremos una seal de una determinada frecuencia, digamos de 1KHz, que tiene un perodo de 1 mS Si del perodo total de 1mS mantenemos la salida a nivel alto durante digamos 0.8mS y a nivel bajo los restantes 0.2mS el LED se iluminar brillantemente. Variando el ciclo de trabajo de la seal y manteniendo fija la frecuencia se puede proporcionar al circuito niveles de potencia variables haciendo que el LED brille mas o menos. Hemos conseguido un control analgico de un modo pramente digital!

El hardware
No se necesita mas que un LED (preferiblemente de los ms brillantes) y una resistencia de 1K. La resistencia y el LED se deben conectar en serie a los pines 2 ( uno de los de salida) y 25 ( masa ) del puerto paralelo. Se puede acceder a los 8 terminales de salida ( del 2 al 9 ) del puerto paralelo por el puerto E/S en la direccin 0x378. Al escribir un 0x01 en el puerto 0x378, el terminal 2 pasar a nivel alto mientras que al escribir 0xFF todos los pines ( 2 .. 9 ) pasan a nivel alto. As es posible controlar el nivel lgico de cualquiera de las salidas mediante la escritura del cdigo correspondiente.

La resistencia sirve para limitar a unos pocos miliamprios la intensidad de la corriente que circula por el circuito. El LED brillar dbilmente si la intensidad que fluye a su travs es muy pequea. Como alternativa puede usarse tambin un transistor como interruptor. (N. del T.: Un LED , acrnimo de Light Emiter Diode , es un dispositivo con polaridad que para que se ilimine debe conectarse su nodo a un potencial elctrico aproximadamente 2 vltios mayor que el de su ctodo, dependiendo del color del LED. Si se conecta invertido no lucir, pero tampoco se daar, al menos en esta aplicacin. A modo orientativo, el electrodo del nodo suele corresponderse con el que tiene forma puntiaguda hacia el interior del cuerpo del LED, y me refiero al dispositivo, no al smbolo.)

El software
La idea bsica es simple, programar una tarea de tiempo real que encender el LED, dormir por algn tiempo, lo apagar y se dormir de nuevo por un tiempo. El tiempo total de conexin mas desconexin es 1mS. Inicialmente el tiempo de conexin sera 0 y el de desconexin 1mS. En la siguiente iteracin el tiempo de conexin ser 1 microsegundo y 999 microsegundos el de desconexin. En la 1000-sima iteracin el tiempo de conexin ser 1mS y 0 el de desconexin y una vez alcanzado este punto, se empieza a reducir el tiempo de conexin gradualmente hasta alcanzar de nuevo los 0 mS y 1mS de tiempo de desconexin. Esto se repetir indefinidamente de modo que en 1 segundo, tiempo en el que se producen 1000 iteraciones, el LED aumentar gradualmente su brillo desde el mnimo al mximo para volver de nuevo al mnimo durante el siguiente segundo. Sigue el cdigo del programa que implementa la idea:

#include #include #include #include

<linux/kernel.h> <linux/module.h> <rtai.h> <rtai_sched.h>

#define STACK_SIZE 4096 #define #define #define #define MIN_ON_PERIOD 0 TOTAL_PERIOD 1000000 /* 1ms */ NSTEPS 1000 STEP_PERIOD 1000

#define LPT1 0x378 static RTIME on_time, off_time, total_period; static RT_TASK my_task; enum direction {DOWN, UP}; static void pwm_task(int n) { int step = 0; static int dir = UP; while(1) { outb(0xff, LPT1); rt_sleep(on_time); outb(0x0, LPT1); rt_sleep(off_time); if(step == NSTEPS) { dir _task);

El cdigo de pwm_task debera ser fcil de comprender. Debido a que RTAI asegura que las tareas de tiempo real cumplirn siempre sus mrgenes temporales a costa de penalizar las otras tareas que no lo sean, podremos ver que el proceso de generacin de la seal PWM contina suavemente incluso cuando el sistema est fuertemente cargado. El paso temporal de 1mS en nuestro cdigo es dificil de cumplir por el RTAI pero ninguna indicacin visual nos permitir observarlo (a menos que usemos un osciloscopio para ver la forma de onda resultante). De todos modos, esto es solo por diversin.

Envo de mensajes
Cada tarea puede enviar mensajes a cualquier otra, siendo estos mensajes simples nmeros enteros. Sigue un programa que muestra el paso de mensajes:
#include <linux/module.h> #include <rtai.h> #include <rtai_sched.h> #define LPT1_BASE 0x378

#define STACK_SIZE 4096 #define TIMERTICKS 1000000000 static RT_TASK tasks[2]; static void task_sender(int t) { int msg = 0xab; RT_TASK *r; r = rt_send(&tasks[1], msg); rt_printk("sender: r = %x\n", r); } static void task_receiver(int t) { int msg; RT_TASK *r; r = rt_receive(&tasks[0], &msg); rt_printk("receiver: msg = %x\n", msg); rt_printk("receiver: r = %x\n", r); } int init_module(void) { RTIME tick_period, now; rt_set_periodic_mode(); rt_task_init(&tasks[0], task_sender, 0, STACK_SIZE, 0, 0, 0); rt_task_init(&tasks[1], task_receiver, 0, STACK_SIZE, 0, 0, 0); rt_printk("sender = %x\n", &tasks[0]); rt_printk("recevier = %x\n", &tasks[1]); tick_period = start_rt_timer(nano2count(TIMERTICKS)); now = rt_get_time(); rt_task_make_periodic(&tasks[1], now + tick_period, tick_period); rt_task_make_periodic(&tasks[0], now + 2*tick_period, tick_period); return 0; } void cleanup_module(void) { stop_rt_timer(); rt_busy_sleep(10000000); rt_task_delete(&tasks[0]); rt_task_delete(&tasks[1]); }

La tarea lectora comienza su ejecucin en el siguiente pulso del temporizador, invocando inmediatamente rt_receive. El primer argumento es la direccin del objeto RT_TASK correspondiente a la tarea que enva, o redactora. Como sta no est an activa, task_receive se bloquea. En el siguiente pulso del temporizador task_sender se activa y enva el mensaje 0xAB a task_receiver. Entoces se desbloquea la tarea receptora imprimiendo el mensaje recibido as como la direccin del objeto RT_TASK correspondiente a la tarea que lo enva.

Uso de mailboxes o buzones.


Un buzn es un mecanismo conveniente mediante el cual varias tareas pueden comunicarse entre s. Veamos el pequeo programa que sigue:
#include <linux/module.h> #include <rtai.h> #include <rtai_sched.h> #define LPT1_BASE 0x378 #define STACK_SIZE 4096 #define TIMERTICKS 1000000000 static RT_TASK tasks[2]; static MBX my_mbx; static void task_sender(int t) { int msg = 0x12cd, r; r = rt_mbx_send(&my_mbx, &msg, sizeof(msg)); rt_printk("sender: r = %d\n", r); } static void task_receiver(int t) { int msg, r; r = rt_mbx_receive(&my_mbx, &msg, sizeof(msg)); rt_printk("receiver: msg = %x\n", msg); rt_printk("receiver: r = %d\n", r); } int init_module(void) { RTIME tick_period, now; rt_set_periodic_mode(); rt_task_init(&tasks[0], task_sender, 0, STACK_SIZE, 0, 0, 0); rt_task_init(&tasks[1], task_receiver, 0, STACK_SIZE, 0, 0, 0); rt_mbx_init(&my_mbx, 4*sizeof(int)); tick_period = start_rt_timer(nano2count(TIMERTICKS)); now = rt_get_time(); rt_task_make_periodic(&tasks[1], now + tick_period, tick_period); rt_task_make_periodic(&tasks[0], now + 2*tick_period, tick_period); return 0; } void cleanup_module(void) { stop_rt_timer(); rt_busy_sleep(10000000); rt_mbx_delete(&my_mbx); rt_task_delete(&tasks[0]); rt_task_delete(&tasks[1]);

Un buzn se representa por una variable esttica de tipo MBX. Se crean mediante la llamada a rt_mbx_init. El segundo argumento es el tamao del buzn. La tarea redactora llama rt_mbx_send y guarda un mensaje 'msg' de tamao sizeof(msg) en el buzn. La tarea receptora obtiene el mensaje llamando a rt_mbx_receive. El receptor se bloquear mientras no se disponga del mensaje completo, o hasta que ocurra algn error.

Conclusion
Empec a explorar Linux/RTAI por curiosidad, pero me tienta investigar ms. Espero que mi entusiasmo sea contagioso y mas lectores comiencen a trastear por propia iniciativa. Pero no olviden comentar sus experimentos. Soy un instructor que trabaja para IC Software en Kerala, India. Me hubiera gustado llegar a ser qumico orgnico, pero en su lugar tom la segunda mejor opcin posible: Linux y la enseanza de la programacin. Copyright 2003, Pramode C.E. Copying license http://linuxgazette.net/copying.html Publicado en el nmero 97 de Linux Gazette, Diciembre 2003

Вам также может понравиться