20.04.2013 Views

Descarga este documento en formato PDF - Ucontrol

Descarga este documento en formato PDF - Ucontrol

Descarga este documento en formato PDF - Ucontrol

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

1 de 22<br />

Sistemas Operativos para<br />

microcontroladores<br />

RTOS<br />

para<br />

PIC<br />

ÍNDICE:<br />

> Introducción<br />

> Pres<strong>en</strong>tación de la guía<br />

> Un poco de teoría sobre los Sistemas Operativos (SO)<br />

> Difer<strong>en</strong>cias <strong>en</strong>tre usar un SO o usar las librerías del compilador de turno<br />

> ¿Qué es un Sistema Operativo?<br />

> Clasificación de los SO<br />

> Introducción al RTOS de CCS<br />

> Funciones del RTOS<br />

> Controlando la ejecución de las tareas<br />

> Yield vs delay<br />

> Coordinar para no dañar<br />

> En sus marcas, listos, ¡FUERA!<br />

> RTOS mail<br />

> Datos de Contacto<br />

> Bibliografía<br />

Ultima actualización:<br />

07/03/2007<br />

INTRODUCCIÓN:<br />

Gracias a la labor del amigo Reinier Torres Labrada, del foro sobre<br />

microcontroladores TODOPIC, ponemos <strong>en</strong> uControl esta<br />

Introducción a los Sistemas Operativos para<br />

Microcontroladores.<br />

Realm<strong>en</strong>te, Reinier esta haci<strong>en</strong>do un muy bu<strong>en</strong> trabajo con <strong>este</strong><br />

tema, y debido a que no hay demasiada información sobre RTOS para<br />

PIC <strong>en</strong> español, es que vamos a ir recopilando <strong>en</strong> esta pagina sus post<br />

sobre el tema. Como podrán ver, abundan los ejemplos, y sus<br />

explicaciones son muy claras.<br />

> Pres<strong>en</strong>tación de la guía:<br />

Las sigui<strong>en</strong>tes son palabras de Reinier, pres<strong>en</strong>tando <strong>en</strong> el foro su idea de armar esta Introducción a los<br />

Sistemas Operativos para Microcontroladores:<br />

"Hola amigos<br />

He visto que <strong>en</strong> el hilo "RTOS para PIC GNU" ha aparecido una interesante controversia sobre los RTOS, sin<br />

embargo lo que más curioso me resulta es que muchos no conoc<strong>en</strong> prácticam<strong>en</strong>te nada sobre el tema, y como<br />

aquí todos estamos para apr<strong>en</strong>der y compartir lo poco que sabemos, quisiera com<strong>en</strong>zar por explicar algunos de<br />

los principios básicos de los SO y como podemos aplicarlos al campo de los microcontroladores.<br />

Espero que esta nueva idea sea de utilidad y despierte el interés <strong>en</strong> muchos de ustedes. Yo soy muy nuevo <strong>en</strong><br />

<strong>este</strong> campo de los SO y los microcontroladores, pero con la ayuda de ustedes podremos hacer muchas cosas<br />

interesantes y b<strong>en</strong>eficiosas para todos.<br />

De hecho todavía no he com<strong>en</strong>zado a utilizar ningún RTOS o algo parecido para meter <strong>en</strong> un PIC, ni siquiera<br />

me he metido con el código de ningún SO, pero <strong>en</strong> la maestría que estoy cursando t<strong>en</strong>go los SO (desde la<br />

óptica del diseño) como asignatura obligada y no quisiera que después de un montón de horas delante de un<br />

profesor que produce más sueño que interés por su asignatura (aunque el tema es interesantísimo), todo ese<br />

conocimi<strong>en</strong>to se quedara <strong>en</strong> la nota al final del semestre.<br />

Bu<strong>en</strong>o amigos... sin más muela (preámbulo) com<strong>en</strong>cemos a trabajar"<br />

[Volver al Índice]<br />

> Un poco de teoría sobre los Sistemas Operativos (SO)<br />

Haci<strong>en</strong>do un poco de historia, y los más añejaditos lo sab<strong>en</strong> mejor que yo que soy un niño de 26, los SO<br />

aparecieron más o m<strong>en</strong>os por allá por los 60.<br />

Lo que ocurrió por aquel <strong>en</strong>tonces, cuando las computadoras, eran tan “poderosas” como nuestros PIC’s<br />

(algunos de gama alta son hasta más poderosos que aquellas computadoras), era que muchos programadores<br />

(matemáticos locos la mayoría), no estaban interesados <strong>en</strong> conocer los detalles de las interfaces, controladores


2 de 22<br />

(matemáticos locos la mayoría), no estaban interesados <strong>en</strong> conocer los detalles de las interfaces, controladores<br />

y demás cacharros electrónicos que adornan a una computadora, y por otro lado algunos programadores ya<br />

habían escrito controladores para esos dispositivos y por tanto los que v<strong>en</strong>ían detrás simplem<strong>en</strong>te querían<br />

utilizarlos y ya (algo parecido a lo que hacemos <strong>en</strong> el foro).<br />

Pues bi<strong>en</strong>, con el tiempo se acumularon programas útiles para manejar dispositivos y tareas complejas que<br />

debían programar personas no interesadas <strong>en</strong> la electrónica, <strong>en</strong>tonces la g<strong>en</strong>te, que trabajaba <strong>en</strong> equipo,<br />

organizó todo aquello de manera que los programadores, los programas y el hardware (HW) pudieran coexistir<br />

sin que se produjera una guerra hubiteana (guerra <strong>en</strong> la que los hombres y los bits se pelean hasta que los<br />

hombres se muer<strong>en</strong> de alguna clase de infarto o pierd<strong>en</strong> a la mujer). Surgieron así los primeros int<strong>en</strong>tos de<br />

crear un SO y los hombres mantuvieron la Paz sobre la tierra y también la guerra “fría”, aunque las cosas se<br />

cal<strong>en</strong>taran a intervalos.<br />

Pasó el tiempo y llegó Bill Gates con DOS y luego Windows y Steve Jobs con su Mac, unos tipos más listos y<br />

cabrones que ing<strong>en</strong>ieros o programadores…. y después también apareció Linus Torvalds con Linux, uno que<br />

estaba más loco que El Quijote y <strong>en</strong>tonces las personas corri<strong>en</strong>tes creyeron que <strong>en</strong> el mundo solo exist<strong>en</strong> a lo<br />

sumo tres SO (Windows, Mac, y Linux), pero no es así hay un montón de SO, algunos muy poderosos y otros<br />

no tanto, dep<strong>en</strong>di<strong>en</strong>do de <strong>en</strong> que lugar y para que se hayan diseñado.<br />

Como es lógico suponer, <strong>en</strong> la parte de la historia que les acabo de contar está el por qué los SO son útiles,<br />

pero eso lo iremos vi<strong>en</strong>do poco a poco porque hay muchas formas <strong>en</strong> las que un SO nos puede servir. Vamos a<br />

ver algunas:<br />

1) Un SO convierte a las computadoras <strong>en</strong> equipos útiles.<br />

Pone una o varias capas de Software (SW) sobre el HW, y con eso podemos escribir programas y utilizar<br />

programas ya escritos por otros para hacer otros programas escritos por nosotros mismos, sin t<strong>en</strong>er que<br />

meternos <strong>en</strong> detalles tales como “bu<strong>en</strong>o ahora mi procesador de textos ya trabaja ¿y como guardo el<br />

<strong>docum<strong>en</strong>to</strong>?”. Seguram<strong>en</strong>te si usted le dice al programador del procesador de texto que para guardar el<br />

<strong>docum<strong>en</strong>to</strong> ti<strong>en</strong>e que tirarse meses escribi<strong>en</strong>do las rutinas que guardan el doc. seguram<strong>en</strong>te le da un ataque.<br />

2) Un SO es una herrami<strong>en</strong>ta poderosa <strong>en</strong> la gestión de procesos.<br />

Nosotros mismos cuando programamos los PIC´s t<strong>en</strong>emos que leer el puerto serie, escribir <strong>en</strong> una LCD,<br />

controlar el motor y todas esas cosas “las hacemos a la vez”. Pero cada una de esas tareas podemos<br />

considerarla un proceso, y un SO puede ayudarnos a gestionarlos efici<strong>en</strong>tem<strong>en</strong>te, aprovechando al máximo el<br />

procesador. Por ejemplo <strong>en</strong> vez de poner al PIC a esperara a que el motor se mueva un poco poni<strong>en</strong>do una<br />

demora, podemos utilizar ese tiempo <strong>en</strong> ir escribi<strong>en</strong>do un poco <strong>en</strong> la LCD y después volver a donde nos<br />

quedamos con el motor.<br />

3) Un SO nos ayuda a diseñar más rápido sistemas mas complejos.<br />

Imagín<strong>en</strong>se ahora una aplicación donde t<strong>en</strong>gamos que hacer cálculos, <strong>en</strong>c<strong>en</strong>der y apagar cosas, comunicarnos<br />

con otros dispositivos y otras tareas más, y que además t<strong>en</strong>gamos que hacerlo rápido y trabajando con otras<br />

personas. Entonces el concepto de divide y v<strong>en</strong>cerás nos pude ser útil y un SO es el arma que podemos utilizar<br />

para derrotar a nuestros <strong>en</strong>emigos: el tiempo y la complejidad. Pues sí nos auxiliamos del SO para dividir y<br />

gestionar los procesos, y con ello no eliminamos la complejidad pero le damos una vuelta elegante, y no<br />

matamos al tiempo pero le damos m<strong>en</strong>os oportunidad de que sea él el que nos mate y si no que el jefe nos<br />

bote.<br />

4) Un SO nos puede ayudar a hacer sistemas más estables.<br />

Ahora t<strong>en</strong>emos unas rutinas que demoran cierto tiempo <strong>en</strong> hacer unos cálculos y como somos listos, hemos<br />

activado el WatchDog (WD) para que si la cosa se cuelga <strong>en</strong> el campo se reinicie el sistema. Pero ¿que pasa si<br />

la rutina demora más de lo debido y el WatchDog nos reinicia sin que haya ningún problema real?. Otra vez la<br />

gestión de recursos nos puede ayudar <strong>en</strong> eso, ya que si nuestra tarea demora más de lo debido el SO puede<br />

quitarle el procesador para at<strong>en</strong>der al WatchDog y darle el procesador a otra tarea que también lo necesita por<br />

algún tiempo. Este caso también sirve cuando aún sin el WD ponemos una llamada a una función que se queda<br />

esperando indefinidam<strong>en</strong>te por que ocurra algo que no ocurre y el código que le sigue no se ejecuta <strong>en</strong> los<br />

tiempos establecidos.<br />

Bu<strong>en</strong>o amigos, hasta aquí hemos visto algunas de las cosas <strong>en</strong> que puede ayudarnos un SO, y sería<br />

maravilloso poder contar con esas herrami<strong>en</strong>tas para programar nuestros PIC. Más adelante seguiremos<br />

apr<strong>en</strong>di<strong>en</strong>do un poco sobre la teoría de los SO, para después meterle el cuerpo a los programas.<br />

Hay algunos libros interesantes sobre los SO, les recomi<strong>en</strong>do uno que puede ayudarles mucho <strong>en</strong> esta primera<br />

parte: Sistemas Operativos Diseño e Implem<strong>en</strong>tación. Andrew S. Tan<strong>en</strong>baum.<br />

[Volver al Índice]<br />

> Difer<strong>en</strong>cias <strong>en</strong>tre usar un SO o usar las librerías del compilador de turno.<br />

Nocturno, un integrante del foro, le plantea a Reinier la sigui<strong>en</strong>te pregunta, que es aprovechada para avanzar<br />

<strong>en</strong> la explicación de los SO:


3 de 22<br />

<strong>en</strong> la explicación de los SO:<br />

"Por lo que leo <strong>en</strong> el capítulo 1 de tu artículo, no veo difer<strong>en</strong>cias <strong>en</strong>tre usar un SO o usar las librerías del<br />

l<strong>en</strong>guaje que estés utilizando <strong>en</strong> cada mom<strong>en</strong>to. No me cabe duda que un SO t<strong>en</strong>drá muchas más aportaciones<br />

que las librerías, ¿pero cuál es la principal v<strong>en</strong>taja que los difer<strong>en</strong>cia?"<br />

Desde el punto de vista de la programación no estás haci<strong>en</strong>do otra cosa que utilizar las librerías del l<strong>en</strong>guaje u<br />

otras creadas por tí o por un bu<strong>en</strong> amigo que te las haya cedido. De hecho si revisamos, por ejemplo, el RTOS<br />

de CCS y las funciones que brinda el l<strong>en</strong>guaje para desarrollar aplicaciones utilizando esta pot<strong>en</strong>cialidad del<br />

l<strong>en</strong>guaje, nos damos cu<strong>en</strong>ta, que por ejemplo, la directiva #use RTOS lo que hace es indicarle al compilador<br />

que ponga <strong>en</strong> el programa final, creado por el compilador, el código del dispatcher (luego vamos a ver que es)<br />

del RTOS, y luego nos ofrece unas funciones de librería como por ejemplo RTOS_WAIT( ), RTOS_ENABLE( ),<br />

RTOS_RUN( ), etc, que nos ayudan a gestionar nuestras tareas (también veremos el concepto y la<br />

implem<strong>en</strong>tación <strong>en</strong> el futuro). Bi<strong>en</strong> hasta ahora nada nuevo, ninguna v<strong>en</strong>taja, y una desv<strong>en</strong>taja: consumo de<br />

memoria de programa para el dispatcher y complicaciones de la vida del programador.<br />

Sin embargo la v<strong>en</strong>taja del SO radica <strong>en</strong> que le permite al programador contar con una herrami<strong>en</strong>ta para<br />

gestionar varias cosas fundam<strong>en</strong>tales <strong>en</strong> el sistema: el procesador, la memoria, y los periféricos, <strong>en</strong> nuestro<br />

caso eso sería todo nuestro flamante PIC. De todos ellos el más importante es el procesador, ya que es quién<br />

hace la mayor parte del trabajo, el objetivo de nuestro SO es mant<strong>en</strong>er ocupado al procesador con trabajo útil,<br />

por ejemplo una demora por SW es hacer trabajar al procesador con trabajo inútil y ese tiempo que se la pasa<br />

saltando de aquí para allá lo podemos utilizar <strong>en</strong> hacer otras cosas y la memoria de programas también,<br />

después veremos como hacer eso.<br />

Supongamos ahora que estamos at<strong>en</strong>di<strong>en</strong>do un teclado, que no está conectado a ninguna fu<strong>en</strong>te de<br />

interrupción, no queda más remedio que <strong>en</strong>cuestar a nuestro teclado para saber si han oprimido o soltado una<br />

tecla, si es así, <strong>en</strong>tonces t<strong>en</strong>emos que hacernos cargo de los rebotes, etc. Hay muchas formas de hacer eso,<br />

una de ellas sería:<br />

1-Espera que se oprima o suelte una tecla<br />

2-Espera un tiempo a que termine el rebote<br />

3-Lee el teclado<br />

4-Procesa la tecla<br />

5-Hacer algo con el código del teclado<br />

6-Regresa al punto 1<br />

Pero una forma s<strong>en</strong>cilla utilizando un SO t<strong>en</strong>dría la sigui<strong>en</strong>te forma:<br />

1-Espera que se oprima o suelte una tecla<br />

2-Me dormiré hasta que termine el rebote<br />

3-Lee el teclado<br />

4-Procesa la tecla<br />

5-Manda un m<strong>en</strong>saje (el código del teclado) a la función adecuada<br />

6-Regresa al punto 1.<br />

Ahora vemos que la cosa cambió ligeram<strong>en</strong>te porque le hemos dicho a algui<strong>en</strong>, me voy a dormir un tiempo,<br />

despiértame cuando pase ese tiempo, y cuando t<strong>en</strong>go el dato o código de la tecla, le mando un m<strong>en</strong>saje a<br />

algui<strong>en</strong> (una función), no llamo directam<strong>en</strong>te a la función, pongo el dato <strong>en</strong> algún lugar e indico que hay un<br />

dato, es asunto de ese algui<strong>en</strong> tomarlo, procesarlo y notificarlo. Esas dos acciones las hago sirviéndome del SO<br />

y le dejo a él el problema de despertarme, y poner el m<strong>en</strong>saje donde corresponde, sin embargo <strong>en</strong> <strong>este</strong><br />

problema el SO puede hacer algo más que <strong>en</strong> principio no está bajo nuestro control.<br />

Como vemos nuestro programa se queda siempre haci<strong>en</strong>do lo mismo <strong>en</strong> un lazo infinito, <strong>en</strong>tonces podemos<br />

decirle al SO: "Oye, <strong>este</strong> código debe ejecutarse todo <strong>en</strong> tal tiempo". Entonces, cuando se cumpla el plazo<br />

establecido, el SO le quita el procesador a ese que se lo quiere coger todo para él solo y se lo da a otra función<br />

que hace algo parecido o para que se ati<strong>en</strong>da una interrupción, etc. cuando todo el mundo haya recibido su<br />

poco de tiempo de procesador, le dice a la función que ati<strong>en</strong>de el teclado: toma el procesador por el tiempo<br />

que te corresponde y así todo el mundo puede t<strong>en</strong>er el procesador para el solo durante un tiempo, es decir<br />

ponemos a un vigilante a que reparta equitativam<strong>en</strong>te el tiempo del procesador.<br />

Hasta aquí hemos visto algunas de las v<strong>en</strong>tajas, más adelante veremos muchas más, sin embargo todavía no<br />

hemos terminado con la introducción teórica a los SO, pero para los más av<strong>en</strong>tajados esto puede aclarar<br />

algunas de sus dudas. Yo se que cosas como esta se pued<strong>en</strong> hacer blandi<strong>en</strong>do interrupciones a derecha e<br />

izquierda, y con otras técnicas, pero como veremos más adelante los SO nos ayudan <strong>en</strong> otras cosas, mi<strong>en</strong>tras<br />

aprovechamos al máximo nuestros preciados recursos del PIC.<br />

[Volver al Índice]<br />

> ¿Qué es un Sistema Operativo?<br />

Según Tan<strong>en</strong>baum, una de las personalidades más reconocidas <strong>en</strong> el mundo académico de los SO, no es fácil


4 de 22<br />

Según Tan<strong>en</strong>baum, una de las personalidades más reconocidas <strong>en</strong> el mundo académico de los SO, no es fácil<br />

dar una definición definitiva de lo que es un SO. El problema consiste <strong>en</strong> que los SO ti<strong>en</strong><strong>en</strong> la característica de<br />

comportarse para el usuario (que puede ser una persona cualquiera, un programador, o un programa de<br />

computadora), como un tipo con "doble personalidad".<br />

Veamos esto con más det<strong>en</strong>imi<strong>en</strong>to:<br />

1) El SO como máquina ext<strong>en</strong>dida:<br />

En esta faceta de la personalidad del SO, la característica destacable es simplificar al programador los detalles<br />

del funcionami<strong>en</strong>to de los dispositivos conectados al sistema.<br />

Esta característica se pone también de manifiesto <strong>en</strong> aplicaciones donde no se usan los SO, un ejemplo típico<br />

son las funciones output_x() e input_x() del compilador CCS. Un programador puede utilizar estas funciones<br />

sin conocer que para que los datos se pongan <strong>en</strong> los pines o se lean, hay que cambiar varios registros <strong>en</strong> el<br />

uC. Se dice que estas funciones son una abstracción del proceso de E/S <strong>en</strong> puerto. Esto es bu<strong>en</strong>o porque<br />

ayuda a los programadores a desarrollar soluciones más rápidam<strong>en</strong>te y con m<strong>en</strong>or probabilidad de errores ya<br />

que si la función está bi<strong>en</strong> escrita es poco probable que falle.<br />

La función de la máquina ext<strong>en</strong>dida es ofrecer al programador una "interfaz" gracias a la cual se utilizan los<br />

recursos del sistema, sin t<strong>en</strong>er que profundizar demasiado <strong>en</strong> los detalles del funcionami<strong>en</strong>to de sus difer<strong>en</strong>tes<br />

compon<strong>en</strong>tes. Esta interfaz que el SO ofrece al programador o el usuario, se conoce comúnm<strong>en</strong>te como<br />

Llamadas al Sistema o API (Aplication Programmer Interface).<br />

Sin embargo esta visión de los sistemas operativos es poco aplicable a nuestro <strong>en</strong>torno, <strong>en</strong> el s<strong>en</strong>tido <strong>en</strong> que<br />

hoy se clasifican a las llamadas al sistema, ya que <strong>en</strong> nuestro mundo todo es pequeño <strong>en</strong> cuanto a capacidad y<br />

el crear una máquina ext<strong>en</strong>dida poderosa consume recursos que usualm<strong>en</strong>te no t<strong>en</strong>dremos. Entonces <strong>en</strong> <strong>este</strong><br />

caso la máquina ext<strong>en</strong>dida queda limitada a algunas llamadas a funciones del SO y al uso de las librerías que<br />

hasta el mom<strong>en</strong>to hemos utilizado habitualm<strong>en</strong>te.<br />

2) El SO como administrador de recursos:<br />

En <strong>este</strong> caso el SO, se comporta como un administrador de nuestros recursos. La v<strong>en</strong>taja de t<strong>en</strong>er algui<strong>en</strong> que<br />

administre efici<strong>en</strong>tem<strong>en</strong>te los recursos consiste <strong>en</strong> que el SO ofrezca al usuario un conjunto de reglas para<br />

compartir y hacer uso de los recursos disponibles con eficacia y efici<strong>en</strong>cia.<br />

Un ejemplo de administración de los recursos es el uso de la CPU, <strong>en</strong> un sistema sin SO, el programador ti<strong>en</strong>e<br />

que estar muy p<strong>en</strong>di<strong>en</strong>te de la implem<strong>en</strong>tación de su sistema, porque puede que determinados requisitos<br />

temporales <strong>en</strong> la ejecución de algunas funciones t<strong>en</strong>gan que cumplirse.<br />

En nuestro caso lo usual es que nos rompamos el coco manipulando interrupciones, poni<strong>en</strong>do demoras,<br />

cambiando contadores y chequeando banderas… ¡Uf solo de p<strong>en</strong>sarlo me dan ganas de llorar! Sin embargo un<br />

SO puede hacerse cargo de todos esos temas de manera eficaz y efici<strong>en</strong>te, incluso ahorrando memoria y<br />

tiempo, y nosotros los programadores conc<strong>en</strong>trarnos <strong>en</strong> la implem<strong>en</strong>tación de la solución, más que <strong>en</strong> la<br />

gestión efici<strong>en</strong>te de nuestros recursos.<br />

Por supuesto que el SO no es mago ni adivino, para ello debe ofrecernos un conjunto de mecanismos,<br />

relativam<strong>en</strong>te s<strong>en</strong>cillos, que nos ayud<strong>en</strong> a "indicarle" o "pedirle" que es lo que queremos hacer.<br />

En el caso de los uC, las implem<strong>en</strong>taciones de los SO, se caracterizan por pot<strong>en</strong>ciar la administración de<br />

recursos del SO, por lo que es esta la faceta de personalidad que más a m<strong>en</strong>udo <strong>en</strong>contraremos <strong>en</strong> los RTOS.<br />

[Volver al Índice]<br />

> Clasificación de los SO.:<br />

A continuación veremos como se clasifican los SO <strong>en</strong> cuanto a dos características es<strong>en</strong>ciales: la administración<br />

del recurso fundam<strong>en</strong>tal (el procesador) y el destino del SO.<br />

Los SO se pued<strong>en</strong> clasificar de distintas maneras, pero para abreviar lo más posible, solam<strong>en</strong>te me voy a<br />

referir a las ya m<strong>en</strong>cionadas.<br />

En cuanto a la administración del procesador exist<strong>en</strong> dos clasificaciones:<br />

1) SO cooperativo (no preemptive):<br />

En <strong>este</strong> caso es el programador quién ti<strong>en</strong>e la responsabilidad de <strong>en</strong>tregarle el procesador al núcleo del SO,<br />

para que éste lo <strong>en</strong>tregue a la próxima tarea que esté solicitándolo o programada para ejecutarse. Es<br />

<strong>en</strong>tonces, muy importante, que las llamadas a funciones que ejecutemos nunca se qued<strong>en</strong> esperando mucho<br />

tiempo por determinados ev<strong>en</strong>tos, evitar los lazos infinitos y <strong>en</strong>tregar el procesador cuando no lo necesitemos,<br />

para que otra tarea o proceso pueda utilizarlo.<br />

2) SO de tiempo compartido (preemptive):<br />

En <strong>este</strong> caso el programador debe contar con los mismos mecanismos que <strong>en</strong> el anterior, pero el SO ti<strong>en</strong>e la<br />

facultad de quitarle el procesador y dárselo a otro si usted se ha excedido <strong>en</strong> su tiempo o hay algui<strong>en</strong> que ti<strong>en</strong>e


5 de 22<br />

facultad de quitarle el procesador y dárselo a otro si usted se ha excedido <strong>en</strong> su tiempo o hay algui<strong>en</strong> que ti<strong>en</strong>e<br />

mayor prioridad.<br />

En cuanto al destino hay unas cuantas clasificaciones pero me voy a conc<strong>en</strong>trar <strong>en</strong> los RTOS. Un RTOS<br />

(Sistema Operativo de Tiempo Real) es un sistema operativo concebido para dispositivos pequeños como los<br />

uC. Aunque el concepto de "tiempo real" es muy controversial, la idea es ejecutar determinadas tareas de<br />

forma que parece que cada tarea se está ejecutando <strong>en</strong> un sistema indep<strong>en</strong>di<strong>en</strong>te, donde el procesador y el<br />

resto de los recursos son sólo para ella.<br />

Los RTOS pued<strong>en</strong> ser utilizados también para computadoras grandes, pero la idea sigue si<strong>en</strong>do la misma,<br />

ejecutar las tareas cumpli<strong>en</strong>do estrictam<strong>en</strong>te con los requisitos temporales de cada una, sin violaciones de<br />

ninguna índole. Otro caso de SO, son los de propósito g<strong>en</strong>eral como UNIX, LINUX, Windows, <strong>en</strong> <strong>este</strong> caso a<br />

difer<strong>en</strong>cia de los RTOS, las tareas cambian de prioridades <strong>en</strong> función de satisfacer las exig<strong>en</strong>cias de los<br />

humanos que actúan como usuarios, no importa si algunas cosas se ejecutan o no cumpli<strong>en</strong>do tiempos<br />

estrictos.<br />

En la próxima <strong>en</strong>trega ya t<strong>en</strong>dremos código para ejecutar y ver como funciona un RTOS.<br />

[Volver al Índice]<br />

> Introducción al RTOS de CCS<br />

Después de teorizar un poco sobre los Sistemas Operativos, vamos a introducirnos <strong>en</strong> la programación de<br />

aplicaciones empleando un RTOS.<br />

En nuestro caso, para com<strong>en</strong>zar, utilizaremos el RTOS que vi<strong>en</strong>e con el compilador de CCS. Las razones de mi<br />

elección las expongo a continuación:<br />

- S<strong>en</strong>cillez <strong>en</strong> la implem<strong>en</strong>tación de aplicaciones.<br />

- El RTOS está integrado <strong>en</strong> el propio compilador de CCS.<br />

- Abundante experi<strong>en</strong>cia <strong>en</strong> el foro con <strong>este</strong> compilador.<br />

- Gran cantidad de dispositivos soportados por el compilador de CCS.<br />

La versión del compilador que t<strong>en</strong>go instalada <strong>en</strong> estos mom<strong>en</strong>tos es la 3.249, así que si <strong>en</strong> versiones<br />

posteriores han aparecido nuevas características, les ruego que lo inform<strong>en</strong> para tomar las debidas<br />

provid<strong>en</strong>cias <strong>en</strong> el mom<strong>en</strong>to oportuno, y com<strong>en</strong>zar a utilizar esas nuevas características.<br />

El RTOS de CCS es un Sistema Operativo de Tiempo Real que implem<strong>en</strong>ta la técnica de multiprocesami<strong>en</strong>to<br />

cooperativo (non preemptive), por lo que es responsabilidad del programador asegurarse de que el control del<br />

procesador retorna al planificador de tareas <strong>en</strong> algún mom<strong>en</strong>to. Así que cuando programemos nuestra<br />

aplicación, t<strong>en</strong>emos que asegurarnos de que no llamamos a una función que se queda esperando por algún<br />

ev<strong>en</strong>to largo como es el caso de gets(), o d<strong>en</strong>tro de un lazo infinito o demasiado ext<strong>en</strong>so.<br />

Planificador de tareas<br />

Uno de los elem<strong>en</strong>tos fundam<strong>en</strong>tales de cualquier SO es el planificador de tareas, éste señor es el<br />

administrador de nuestros recursos. Su misión fundam<strong>en</strong>tal es determinar d<strong>en</strong>tro de las tareas que están listas<br />

para ejecutarse, a cuál de ellas le <strong>en</strong>trega el procesador. La política de planificación empleada por CCS no la<br />

conozco, pero eso no importa porque el RTOS funciona y para lo que queremos hacer, nos sirve bi<strong>en</strong>.<br />

Directivas del preprocesador<br />

Exist<strong>en</strong> dos directivas del preprocesador para el uso del RTOS, ellas son:<br />

#USE RTOS: Se utiliza para indicarle al compilador que se va a utilizar el RTOS<br />

#TASK: Se utiliza para indicarle al compilador se la función definida a continuación es una tarea a ejecutar por<br />

el RTOS<br />

Vamos a ver más det<strong>en</strong>idam<strong>en</strong>te cada una de las directivas, así como sus parámetros de configuración:<br />

#USE RTOS : Opciones del RTOS:<br />

timer: especifica que temporizador, de los disponibles, es el que se utilizará para la ejecución de las tareas.<br />

Este temporizador solam<strong>en</strong>te debe ser utilizado por el RTOS y típicam<strong>en</strong>te se escoge Timer0.<br />

minor_cycle: especifica la cantidad de tiempo mínima que una tarea t<strong>en</strong>drá para ejecutarse, y los tiempos de<br />

ejecución de cada tarea deb<strong>en</strong> ser múltiplos de esta cantidad. Si por ejemplo decimos que el tiempo mínimo de<br />

ejecución para todas las tareas es de 1ms, debemos conocer que cada tarea se ejecutará, <strong>en</strong> m<strong>en</strong>os tiempo<br />

que <strong>este</strong>. Lo realm<strong>en</strong>te importante de <strong>este</strong> dato es que ayuda a establecer la frecu<strong>en</strong>cia con que se ejecutan<br />

las tareas, luego veremos un ejemplo de esto. Este parámetro, si no se especifica, es calculado por el<br />

compilador <strong>en</strong> el mom<strong>en</strong>to de la compilación.<br />

statistics: le indica al compilador que lleve las estadísticas de las tareas, esto sirve para conocer que tiempo


6 de 22<br />

statistics: le indica al compilador que lleve las estadísticas de las tareas, esto sirve para conocer que tiempo<br />

consume cada tarea <strong>en</strong> ejecutarse, sin embargo como veremos más adelante, la estadística realm<strong>en</strong>te<br />

importante es la que nos indica si nuestra tarea se ha sobrepasado <strong>en</strong> su tiempo mínimo de ejecución.<br />

#TASK: Opciones para las tareas:<br />

rate: especifica con que frecu<strong>en</strong>cia se ejecuta la tarea, <strong>este</strong> parámetro debe ser igual a minor_cycle de<br />

#use_rtos o un múltiplo de <strong>este</strong> valor.<br />

max: especifica que cantidad de tiempo debe consumir esta tarea <strong>en</strong> su ejecución, si se sobrepasa <strong>este</strong> tiempo<br />

y están activadas las estadísticas, <strong>en</strong>tonces esta tarea es marcada con el valor overrun. Este parámetro es útil<br />

para informar al programador que una tarea se ha pasado de su tiempo de ejecución, y si el RTOS fuera de<br />

tiempo compartido seguram<strong>en</strong>te especificaría el tiempo <strong>en</strong> que el planificador le retira el procesador para<br />

dárselo a otra tarea.<br />

queue: especifica el tamaño de la cola de m<strong>en</strong>sajes de la tarea. Si la tarea no recibe m<strong>en</strong>sajes, <strong>en</strong>tonces debe<br />

dejarse <strong>en</strong> blanco para no consumir memoria RAM innecesariam<strong>en</strong>te.<br />

Hasta aquí hemos visto una pequeña explicación de las directivas para utilizar el RTOS. Sin embargo, la<br />

utilidad de esto es mejor verla con un ejemplo.<br />

[Volver al Índice]<br />

> Funciones del RTOS<br />

EL RTOS de CCS ofrece un conjunto de funciones que veremos cada una <strong>en</strong> su mom<strong>en</strong>to y con sus debidos<br />

ejemplos. Sin embargo hoy utilizaremos solam<strong>en</strong>te la función rtos_run(), que le indica al planificador que<br />

comi<strong>en</strong>ce a ejecutar las tareas.<br />

El ejemplo:<br />

Se quiere implem<strong>en</strong>tar una aplicación <strong>en</strong> un PIC16F877, donde se utilice el RTOS para transmitir por el puerto<br />

serie de <strong>este</strong> uC, tres cad<strong>en</strong>as de caracteres. Cada cad<strong>en</strong>a será transmitida desde d<strong>en</strong>tro de una tarea del<br />

RTOS y t<strong>en</strong>drán el <strong>formato</strong> “Ejecutada tarea #”. Las especificaciones de tiempo del sistema y de cada tarea<br />

son las sigui<strong>en</strong>tes:<br />

Temporizador para el RTOS: Timer0<br />

Tiempo mínimo <strong>en</strong> que debe ejecutarse una tarea: 10ms<br />

Frecu<strong>en</strong>cia de ejecución de la Tarea 1: 1seg, tiempo para ejecutar: 10ms<br />

Frecu<strong>en</strong>cia de ejecución de la Tarea 2: 2seg, tiempo para ejecutar: 5ms<br />

Frecu<strong>en</strong>cia de ejecución de la Tarea 3: 3seg, tiempo para ejecutar: 250us<br />

El código lo pongo a continuación y posteriorm<strong>en</strong>te les doy una explicación:<br />

Código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

#use RTOS(timer=0, minor_cycle=10ms) //temporizador Timer0, tiempo mínimo de ejecución de cada tarea 10ms<br />

int8 test;<br />

//Definición de las prototipos de función de las tareas<br />

#task (rate=1s, max=10ms) //Ejecutar cada 1 segundo y consumir como máximo 10ms<br />

void Tarea1();<br />

#task (rate=2s, max=5ms) //Ejecutar cada 2 segundos y consumir como máximo 5ms<br />

void Tarea2();<br />

#task (rate=3s, max=250us) //Ejecutar cada 3 segundo y consumir como máximo 250us<br />

void Tarea3();<br />

void main()<br />

{<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run(); //A partir de aquí com<strong>en</strong>zará la ejecución de las tareas<br />

}<br />

//Implem<strong>en</strong>tación de las tareas<br />

void Tarea1()<br />

{<br />

printf("Ejecutada tarea 1\r");<br />

}


7 de 22<br />

void Tarea2()<br />

{<br />

printf("Ejecutada tarea 2\r");<br />

}<br />

void Tarea3()<br />

{<br />

printf("Ejecutada tarea 3\r");<br />

}<br />

Este código funciona y si lo simulan con el Proteus comprobarán que las cad<strong>en</strong>as sal<strong>en</strong> por el puerto serie.<br />

Como pued<strong>en</strong> ver es un ejemplo s<strong>en</strong>cillo pero que muestra el funcionami<strong>en</strong>to del RTOS. Al ejecutarlo pued<strong>en</strong><br />

comprobar que primero se ejecuta la Tarea 1, pero después comprobarán que las tareas no se ejecutan <strong>en</strong><br />

ord<strong>en</strong> secu<strong>en</strong>cial porque la Tarea 2 se ejecutará cada 2 segundos y la Tarea 3 cada 3 segundos.<br />

La no ejecución secu<strong>en</strong>cial de cada tarea se debe al parámetro rate de la directiva #task, que le dice al<br />

planificador con que frecu<strong>en</strong>cia ejecutar cada tarea. Este programa puede ser fácilm<strong>en</strong>te modificado para<br />

cualquier aplicación específica, un ejemplo que se me ocurre es la lectura de teclados y eliminación de rebotes.<br />

[Volver al Índice]<br />

> Controlando la ejecución de las tareas<br />

En la pasada <strong>en</strong>trega sobre programación con el RTOS de CCS vimos como es que el planificador programaba<br />

la ejecución de las tareas que estaban activas para ser ejecutadas. Pero como es lógico <strong>en</strong> todo mom<strong>en</strong>to las<br />

tareas no ti<strong>en</strong><strong>en</strong> por que <strong>en</strong>contrarse <strong>en</strong> condiciones de ser ejecutadas, a veces hace falta poner a “dormir”<br />

una tarea y “despertarla” cuando hace falta que se ejecute.<br />

Para lograr <strong>este</strong> objetivo el RTOS de CCS nos ofrece dos funciones: RTOS_DISABLE( ) y RTOS_ENABLE( )<br />

Al crear una tarea el RTOS la marca como activa (<strong>en</strong>able), y cuando comi<strong>en</strong>za a ejecutarse el planificador de<br />

tareas la pondrá <strong>en</strong> la cola de ejecución para ejecutarla cuando le llega su turno. Sin embargo <strong>en</strong> muchas<br />

ocasiones notaremos que no hace falta ejecutar la tarea hasta que se cumplan ciertas condiciones.<br />

Este es un mecanismo simple, que sin el uso del RTOS controlaríamos mediante una bandera (flag), y la<br />

implem<strong>en</strong>tación de una función a la cual llamaremos si se cumple la condición de su ejecución.<br />

Hasta el mom<strong>en</strong>to el RTOS no nos ofrece ninguna v<strong>en</strong>taja respecto al método tradicional, sin embargo<br />

combinemos esta característica con lo apr<strong>en</strong>dido <strong>en</strong> el ejemplo de la <strong>en</strong>trega anterior y comprobaremos que si<br />

hay v<strong>en</strong>tajas, y por cierto nada despreciables.<br />

La v<strong>en</strong>taja principal con respecto al método tradicional consiste <strong>en</strong> que usted hace la consulta para comprobar<br />

si hay que ejecutar la función, sin embargo ahora solam<strong>en</strong>te le dice al RTOS, habilita a la tarea tal y ponla a<br />

ejecutarse cuando le corresponda y se olvida de todos los problemas asociados respecto al tema de cuando le<br />

corresponde ejecutarse la tarea y demás.<br />

¿Algui<strong>en</strong> se acuerda de los molestos temporizadores, banderas, registros y Dios sabe cuantos <strong>en</strong>g<strong>en</strong>dros de<br />

control de ejecución condicional para una función estándar?<br />

De forma similar a como se le dice al planificador que la tarea tal debe ser ejecutada, podemos avisarle para<br />

que no la ejecute.<br />

Veamos esto con un ejemplo:<br />

T<strong>en</strong>emos una aplicación <strong>en</strong> la que hemos colocado tres LEDs uno rojo <strong>en</strong> RB0, uno verde <strong>en</strong> RB1 y otro<br />

amarillo <strong>en</strong> RB2. Vamos a <strong>en</strong>c<strong>en</strong>der y apagar los LEDs con una frecu<strong>en</strong>cia determinada y a intervalos regulares<br />

según el sigui<strong>en</strong>te esquema:<br />

- Led Rojo parpadea con una frecu<strong>en</strong>cia de 250ms por un período de 5s, el resto apagado.<br />

- Led Verde parpadea con una frecu<strong>en</strong>cia de 350ms por un período de 10s, el resto apagado.<br />

- Led Amarillo parpadea con una frecu<strong>en</strong>cia de 450ms por un período de 15s, el resto apagado.<br />

- Todos los LED parpadean, cada uno con su frecu<strong>en</strong>cia correspondi<strong>en</strong>te durante 5s.<br />

- Com<strong>en</strong>zamos por el LED rojo nuevam<strong>en</strong>te y repetimos el ciclo.<br />

La solución que les propongo es la sigui<strong>en</strong>te:<br />

- 3 tareas para controlar el parpadeo de cada LED y el tiempo que se ejecutan<br />

- 1 tarea para controlar el tiempo <strong>en</strong> que todos los LEDs parpadean.<br />

T<strong>en</strong>emos un total de 4 tareas <strong>en</strong> la aplicación y el control de la ejecución será el sigui<strong>en</strong>te:


8 de 22<br />

T<strong>en</strong>emos un total de 4 tareas <strong>en</strong> la aplicación y el control de la ejecución será el sigui<strong>en</strong>te:<br />

Al inicio solam<strong>en</strong>te la tarea LED_R estará habilitada, una vez que LED_R ha concluido inicia la tarea LED_V y<br />

se autodeshabilita.<br />

Cuando LED_V concluye habilita LED_A y se autodeshabilita.<br />

Cuando LED_V concluye habilita LEDS y se autodeshabilita.<br />

Cuando LEDS se inicia por primera vez habilita LED_R, LED_V y LED_A cuando le toca de nuevo el turno se<br />

autodeshabilita y deshabilita a LED_V y LED_A<br />

Veamos el código:<br />

Código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

//Como el reloj de <strong>este</strong> micro se ha puesto a correr con 20MHz el Timer0 no ti<strong>en</strong>e<br />

//mayor resolución que 10ms, es por eso que el tiempo mínimo de ejecución es de 10ms<br />

#use RTOS(timer=0, minor_cycle=10ms)<br />

int1 iB0, iB1, iB2;<br />

int1 iLEDS = 0;<br />

int8 iCountR = 0;<br />

int8 iCountV = 0;<br />

int8 iCountA = 0;<br />

int8 iCount = 0;<br />

#task (rate=10ms, max=10ms)<br />

void Task_Disabler();<br />

#task (rate=250ms, max=10ms)<br />

void LED_R();<br />

#task (rate=350ms, max=10ms)<br />

void LED_V();<br />

#task (rate=450ms, max=10ms)<br />

void LED_A();<br />

#task (rate=5s, max=10ms)<br />

void LEDS();<br />

void main()<br />

{<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run();<br />

}<br />

//esta es una función trucul<strong>en</strong>ta porque las tareas no pued<strong>en</strong> crearse deshabilitadas<br />

//y no se pued<strong>en</strong> deshabilitar hasta que el RTOS esté funcionando. Lo considero una<br />

//defici<strong>en</strong>cia sustancial de <strong>este</strong> RTOS<br />

void Task_Disabler()<br />

{<br />

rtos_disable(LED_V);<br />

rtos_disable(LED_A);<br />

rtos_disable(LEDS);<br />

rtos_disable(Task_Disabler);<br />

}<br />

//cada tarea ti<strong>en</strong>e un contador para llevar la cantidad de veces que se pasa por la<br />

//función y cuando se cumple el tiempo establecido <strong>en</strong>tonces habilita y deshabilita<br />

//las tareas correspondi<strong>en</strong>tes<br />

void LED_R()<br />

{<br />

iB0 = !iB0;<br />

output_bit( PIN_B0, iB0);<br />

if (iCountR++==20)<br />

{<br />

iCountR = 0;<br />

rtos_disable(LED_R);<br />

rtos_<strong>en</strong>able(LED_V);<br />

}<br />

}


9 de 22<br />

}<br />

void LED_V()<br />

{<br />

iB1 = !iB1;<br />

output_bit( PIN_B1, iB1);<br />

if (iCountV++==27)<br />

{<br />

iCountV = 0;<br />

rtos_disable(LED_V);<br />

rtos_<strong>en</strong>able(LED_A);<br />

}<br />

}<br />

void LED_A()<br />

{<br />

iB2 = !iB2;<br />

output_bit( PIN_B2, iB2);<br />

if (iCountA++==33)<br />

{<br />

iCountA = 0;<br />

rtos_disable(LED_A);<br />

rtos_<strong>en</strong>able(LEDS);<br />

}<br />

}<br />

void LEDS()<br />

{<br />

if(!iLEDS)<br />

{<br />

rtos_<strong>en</strong>able(LED_R);<br />

rtos_<strong>en</strong>able(LED_V);<br />

rtos_<strong>en</strong>able(LED_A);<br />

iLEDS = 1;<br />

}<br />

else<br />

{<br />

rtos_disable(LED_V); //Hay que habilitar y deshabilitar explícitam<strong>en</strong>te<br />

rtos_disable(LED_A); //cada tarea sobre todo LED_R que debe continuar<br />

rtos_disable(LEDS); //ejecutándose otros 5 segundos más<br />

rtos_<strong>en</strong>able(LED_R);<br />

iCountR = 0;<br />

iCountV = 0;<br />

iCountA = 0;<br />

iLEDS = 0;<br />

}<br />

}<br />

Cuando corran y simul<strong>en</strong> el ejemplo verán que a veces los LEDs se quedan <strong>en</strong>c<strong>en</strong>didos o apagados<br />

indistintam<strong>en</strong>te, <strong>este</strong> es un problema del programa, ya que lo deseable sería que los LEDs se quedaran<br />

apagados cuando termina la función. Sin embargo lo he dejado así porque <strong>en</strong> el futuro vamos a ver cómo<br />

podemos hacer esto con las propias funciones del RTOS.<br />

Como la vez anterior, les dejo de tarea hacerlo sin RTOS para ver como les queda, a mi llevó m<strong>en</strong>os de dos<br />

horas elaborar el texto y escribir el código. Me imagino que sin RTOS me tardaría más de un día completo,<br />

consumiría un montón de páginas de explicación y otro montón para el código.<br />

Este es un método simple, pero hay otros mucho más elaborados que iremos vi<strong>en</strong>do poco a poco. La próxima<br />

<strong>en</strong>trega será yield() vs delay(), vamos a ver el método de las esperas efici<strong>en</strong>tes.<br />

[Volver al Índice]<br />

> Yield vs delay<br />

¿Cuantas veces <strong>en</strong> nuestras aplicaciones t<strong>en</strong>emos que situar demoras para esperar la ocurr<strong>en</strong>cia de un ev<strong>en</strong>to<br />

determinado? Por ejemplo, para eliminar rebotes <strong>en</strong> un teclado, esperar a que el conversor AD termine y quién<br />

sabe cuantas cosas más.<br />

Normalm<strong>en</strong>te estas demoras se hac<strong>en</strong> poni<strong>en</strong>do al procesador a decrem<strong>en</strong>tar contadores y dar saltos<br />

recursivos como si fuese un loco. Durante todo el tiempo de la demora, nuestro microcontrolador, estará<br />

ocupado <strong>en</strong> perder el tiempo, y es por eso que a <strong>este</strong> mecanismo se le llama espera ocupada.


10 de 22<br />

ocupado <strong>en</strong> perder el tiempo, y es por eso que a <strong>este</strong> mecanismo se le llama espera ocupada.<br />

Sin embargo un RTOS nos ofrece un conjunto de herrami<strong>en</strong>tas para eliminar <strong>este</strong> molesto inconv<strong>en</strong>i<strong>en</strong>te, el<br />

más s<strong>en</strong>cillo de ellos es aquel que le permite a una tarea decirle al RTOS: ponme a dormir hasta que me toque<br />

de nuevo mi turno de ejecutarme. Para ese efecto el RTOS de CCS implem<strong>en</strong>ta la función rtos_yield().<br />

Este mecanismo es muy bu<strong>en</strong>o puesto que mi<strong>en</strong>tras la tarea “se duerme” nuestro microcontrolador puede<br />

dedicarse a realizar otras tareas útiles y hacer de la espera ocupada una espera efici<strong>en</strong>te. Para la tarea que<br />

está dormida esto no repres<strong>en</strong>ta nada, a ella le da lo mismo ocupar al procesador <strong>en</strong> hacer nada que <strong>en</strong> hacer<br />

algo productivo, sin embargo no ocurre lo mismo para el resto de las tareas que están esperando que se les<br />

<strong>en</strong>tregue el procesador.<br />

Otro caso <strong>en</strong> que yield() nos puede ser útil es para <strong>en</strong>tregar el procesador cuando nos hemos pasado de<br />

tiempo <strong>en</strong> la ejecución de alguna tarea. Ya sabemos que el RTOS de CCS es cooperativo, por lo que si una<br />

tarea consume más tiempo de la cu<strong>en</strong>ta puede hacer que el sistema colapse, ya que hay que <strong>en</strong>tregar<br />

explícitam<strong>en</strong>te el procesador al RTOS para que se lo de a otra tarea.<br />

Sin embargo con la función de las estadísticas habilitadas, podemos comprobar si alguna tarea se ha pasado<br />

de tiempo, y con ello implem<strong>en</strong>tar mecanismos adecuados para que la tarea <strong>en</strong> cuestión reajuste su dinámica<br />

y ceda el procesador oportunam<strong>en</strong>te, para ello podemos auxiliarnos de la función rtos_overrun(). Esta función<br />

no ti<strong>en</strong>e valor si se usa d<strong>en</strong>tro de una tarea para comprobar si ella misma se ha pasado porque la actualización<br />

de las estadísticas se hace después de ceder el procesador, considero que esta es una de las debilidades de<br />

<strong>este</strong> RTOS, <strong>en</strong> ese s<strong>en</strong>tido.<br />

El uso de rtos_overrun(), debería poderse utilizar d<strong>en</strong>tro de una misma tarea para comprobar si desde que me<br />

cedieron el procesador para ejecutar, me he pasado de tiempo o no y <strong>en</strong> consecu<strong>en</strong>cia <strong>en</strong>tregar el procesador<br />

al RTOS.<br />

En el ejemplo que les traigo hoy vamos a emplear rtos_yield() para ceder el procesador y rtos_overrun() para<br />

conocer si una tarea se ha pasado de tiempo.<br />

Sin embargo yield() no es una función realm<strong>en</strong>te poderosa, al m<strong>en</strong>os <strong>en</strong> <strong>este</strong> RTOS, porque pone a dormir a la<br />

tarea durante un período completo del valor rate, que especificamos al declarar la función como una tarea del<br />

RTOS, y eso <strong>en</strong> ocasiones no es lo que deseamos. Aún así es mejor que el procesador de nuestro PIC esté<br />

haci<strong>en</strong>do algo útil y no perdi<strong>en</strong>do el tiempo.<br />

En las <strong>en</strong>tregas futuras veremos otras funciones que nos ofrece <strong>este</strong> RTOS para hacer esperas más efici<strong>en</strong>tes<br />

vinculadas al uso de recursos compartidos <strong>en</strong> nuestras aplicaciones. El uso efici<strong>en</strong>te del procesador, los<br />

recursos del sistema y la no linealidad <strong>en</strong> la ejecución de las tareas <strong>en</strong> un sistema que emplea SO, ha obligado<br />

a los diseñadores de SO a crear mecanismos para proteger los datos y hacer uso de esos recursos de forma<br />

ord<strong>en</strong>ada y segura. Estos mecanismos se clasifican <strong>en</strong> dos grupos: La sincronización y la coordinación que<br />

com<strong>en</strong>zaremos a ver <strong>en</strong> la próxima <strong>en</strong>trega.<br />

Ejemplo:<br />

Implem<strong>en</strong>te <strong>en</strong> un PIC16F877 una aplicación <strong>en</strong> la que se ejecut<strong>en</strong> tres tareas, con las sigui<strong>en</strong>tes<br />

características:<br />

- La tarea No. 1 T<strong>en</strong>drá un contador el cual se increm<strong>en</strong>ta <strong>en</strong> un lazo hasta que alcanza el valor de 1000.<br />

Cuando llegue a ese valor imprime el sigui<strong>en</strong>te m<strong>en</strong>saje: “Tarea contadora completada” y además pone una<br />

bandera a 1, para indicar que ha concluido. Debe colocar <strong>en</strong> esta tarea código para que la tarea ceda el<br />

procesador <strong>en</strong> algún mom<strong>en</strong>to al RTOS. El tiempo máximo de ejecución de esta tarea es de 10ms y debe<br />

ejecutarse cada 30ms.<br />

- La tarea No. 2 debe esperar a que la tarea No. 1 termine para <strong>en</strong>viar por el puerto serie un m<strong>en</strong>saje similar<br />

al de la tarea No. 1, sin embargo, esta tarea también <strong>en</strong>viará, por el puerto serie, un m<strong>en</strong>saje cada vez que le<br />

cede el procesador al RTOS. Esta debe ejecutarse <strong>en</strong> un tiempo de 10ms y debe ejecutarse cada 40ms.<br />

- Por último, existe una tarea que se <strong>en</strong>carga de hacer parpadear un LED conectado <strong>en</strong> RB0, cada 100ms y<br />

<strong>en</strong>viar un m<strong>en</strong>aje por el puerto serie <strong>en</strong> caso de que la Tarea 1 o la Tarea 2 se hayan pasado <strong>en</strong> algún<br />

mom<strong>en</strong>to de su tiempo de ejecución. El tiempo de procesador para esta tarea debe ser de 10ms.<br />

Código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

#use RTOS(timer=0, minor_cycle=10ms, statistics) //se utilizan las estadísticas<br />

//hace falta para usar rtos_overrun()<br />

int32 iT1Counter = 0;<br />

int1 bT1Flag = 0;<br />

int1 bLed = 0;<br />

#task (rate=30ms, max=10ms)


11 de 22<br />

#task (rate=30ms, max=10ms)<br />

void Tarea1();<br />

#task (rate=40ms, max=10ms)<br />

void tarea2();<br />

#task (rate=100ms, max=10ms)<br />

void Tarea3();<br />

void main()<br />

{<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run();<br />

}<br />

void Tarea1()<br />

{<br />

bT1Flag = 0;<br />

for(iT1Counter = 0; iT1Counter Coordinar para no dañar<br />

Hasta ahora hemos visto al RTOS como un elem<strong>en</strong>to que nos ayuda a “simplificar” el código de nuestra<br />

aplicación. Por una parte, dejándole parte de la temporización de las tareas que debe realizar nuestra<br />

aplicación y por otra utilizando el tiempo de las demoras ocupadas para hacer que otras tareas ejecut<strong>en</strong> su<br />

código <strong>en</strong> ese tiempo. Sin embargo esas v<strong>en</strong>tajas no son nada comparado con dos herrami<strong>en</strong>tas


12 de 22<br />

código <strong>en</strong> ese tiempo. Sin embargo esas v<strong>en</strong>tajas no son nada comparado con dos herrami<strong>en</strong>tas<br />

fundam<strong>en</strong>tales que nos ofrec<strong>en</strong> los SO: la coordinación y la sincronización, de ellas hoy vamos a ver solam<strong>en</strong>te<br />

una de ellas: la coordinación.<br />

La coordinación es un término <strong>en</strong> la programación para SO, que se basa <strong>en</strong> la protección de recursos<br />

compartidos, es un concepto ori<strong>en</strong>tado a evitar que difer<strong>en</strong>tes tareas puedan acceder a los datos o recursos y<br />

poner al sistema <strong>en</strong> un estado inestable o inseguro.<br />

Ejemplos que pon<strong>en</strong> de manifiesto el problema de la coordinación hay muchísimos pero <strong>en</strong> aras de mant<strong>en</strong>er el<br />

curso lo más s<strong>en</strong>cillo posible y adecuarlo un poco más a las aplicaciones que normalm<strong>en</strong>te se nos pres<strong>en</strong>tan yo<br />

utilizaré un problema más cercano a nuestro <strong>en</strong>torno.<br />

El problemas de la coordinación también se conoce como el problema de la concurr<strong>en</strong>cia o acceso concurr<strong>en</strong>te<br />

a recursos, a mi me gusta llamarlo “coordinación” para establecer una mejor difer<strong>en</strong>cia con respecto al otro<br />

mecanismo; el de la “sincronización” que veremos <strong>en</strong> la próxima <strong>en</strong>trega.<br />

Destripando un poco más a la coordinación diremos que: "la coordinación es el mecanismo que debe<br />

implem<strong>en</strong>tar un SO para asegurar el acceso seguro a recursos compartidos del sistema, y que no ti<strong>en</strong>e <strong>en</strong><br />

cu<strong>en</strong>ta restricciones temporales". Con esto queda claro que proteger a los recursos de un acceso no seguro es<br />

lo más importante <strong>en</strong> la coordinación, no importa durante que tiempo algui<strong>en</strong> (una tarea) esté utilizando el<br />

recurso, hasta que no lo libere nadie más podrá utilizarlo.<br />

Hay mecanismos de coordinación que implem<strong>en</strong>tan también el problema de la sincronización, que si ti<strong>en</strong>e <strong>en</strong><br />

cu<strong>en</strong>ta el factor tiempo, pero el RTOS de CCS no los implem<strong>en</strong>ta. Esto puede considerarse una limitante o una<br />

v<strong>en</strong>taja, según el tipo de aplicación <strong>en</strong> que se vaya a utilizar.<br />

Un RTOS que implem<strong>en</strong>ta un mecanismo de coordinación con sincronización es el LMOS de Darukur, que<br />

veremos d<strong>en</strong>tro de algún tiempo <strong>en</strong> <strong>este</strong> foro, debidam<strong>en</strong>te docum<strong>en</strong>tado gracias a un proyecto que Darukur y<br />

un servidor, llevaremos a ustedes. Por el mom<strong>en</strong>to <strong>este</strong> simple cursillo es un bu<strong>en</strong> método (no el único) para<br />

acercarse al mundo de los RTOS.<br />

Veamos la coordinación con un ejemplo s<strong>en</strong>cillo pero bi<strong>en</strong> claro:<br />

"Supongamos que una madre ha comprado una camisa muy bonita, ella quería comprar dos, pero <strong>en</strong> la ti<strong>en</strong>da<br />

solo había una así que decidió comprarla de todas formas. Cuando llegó a casa llama a sus hijos (ambos usan<br />

la misma talla de camisa), y les dice: “he comprado esta camisa, pero <strong>en</strong> la ti<strong>en</strong>da solam<strong>en</strong>te había una, así<br />

que deb<strong>en</strong> compartirla como bu<strong>en</strong>os hermanos”.<br />

Las palabras de la madre no son al<strong>en</strong>tadoras porque a ambos les gusta mucho la camisa y sin embargo deb<strong>en</strong><br />

compartirla, <strong>en</strong>tonces la decisión de ambos es colocar la camisa <strong>en</strong> una percha, y cada vez que uno de los dos<br />

decida utilizarla se la ponga (dejando el perchero vacío). Pero hay una regla adicional, si cuando uno de los dos<br />

va a utilizar la camisa el otro ya se la llevó dejará una marca para indicarle al otro hermano que no podrá<br />

utilizar la camisa hasta que el que la marcó haya hecho uso de ella."<br />

Este es un mecanismo <strong>en</strong> que los hermanos se han puesto de acuerdo para utilizar un recurso (la camisa), de<br />

manera compartida (porque es la única), de forma coordinada (para eso se pusieron de acuerdo e hicieron<br />

unas reglas simples).<br />

Para implem<strong>en</strong>tar las reglas mostradas <strong>en</strong> el ejemplo anterior, el RTOS de CCS ti<strong>en</strong>e dos funciones<br />

rtos_wait() y rtos_signal().<br />

Para utilizar estas funciones primero hay que crear una variable <strong>en</strong>tera que hará las funciones de percha y, que<br />

hablando con propiedad, se llama semáforo. El semáforo es el elem<strong>en</strong>to que le permite a la tarea reclamar el<br />

recurso compartido o esperar por él si ya está <strong>en</strong> uso. Las funciones rtos_wait() y rtos_signal() se utilizan<br />

para marcar el mom<strong>en</strong>to de inicio y fin del código que utiliza el recurso compartido. A la sección de código que<br />

utiliza el recurso compartido se le conoce como sección crítica.<br />

Veamos como funciona esto <strong>en</strong> términos de programación:<br />

Usted crea una variable <strong>en</strong>tera que será su semáforo o marcador de uso del recurso compartido.<br />

El recurso compartido puede ser una o varias variables del sistema, <strong>en</strong> <strong>este</strong> caso el recurso compartido es un<br />

recurso de memoria. O puede ser un periférico del sistema, como es el caso del puerto serie o la memoria<br />

EEPROM, o cualquier otro.<br />

rtos_wait() y rtos_signal() son los marcadores de inicio y fin del código que hace uso de nuestro recurso<br />

compartido.<br />

Cuando se inicia el programa usted inicializa el semáforo <strong>en</strong> algún valor positivo que determina la cantidad de<br />

tareas que pued<strong>en</strong> utilizar el recurso al mismo tiempo, normalm<strong>en</strong>te es uno para las tareas que modificarán el<br />

recurso compartido, mi<strong>en</strong>tras que para tareas que solam<strong>en</strong>te le<strong>en</strong> datos puede que no se us<strong>en</strong> secciones<br />

críticas o se permita más de una tarea que acceda simultáneam<strong>en</strong>te al recurso compartido.


13 de 22<br />

Cuando una tarea llegue a la sección de código que hace uso del recurso compartido, debe, primero que nada,<br />

ejecutar la función rtos_wait(sem). Si el semáforo es mayor que cero, el RTOS decrem<strong>en</strong>tará la variable y<br />

permitirá que la tarea continúe su ejecución, sin embargo si el semáforo está <strong>en</strong> cero, el RTOS le quitará el<br />

procesador a la tarea y se lo cederá a otra que le toque ejecutarse. Cuando le corresponda nuevam<strong>en</strong>te a la<br />

tarea que pidió el acceso al recurso compartido, el RTOS comprobará el estado del semáforo, si éste es mayor<br />

que cero, lo decrem<strong>en</strong>tará y le dará el procesador a la tarea para que se siga ejecutando, si no, volverá a<br />

dormir a la tarea hasta el próximo turno y así sucesivam<strong>en</strong>te.<br />

Al final del código de la sección crítica hay que colocar un rtos_signal(sem) para que el RTOS increm<strong>en</strong>te el<br />

semáforo permiti<strong>en</strong>do que otra tarea pueda utilizar el recurso compartido.<br />

El ejemplo de hoy es el sigui<strong>en</strong>te:<br />

Elabore un programa para un PIC16F877 que permita mant<strong>en</strong>er actualizada la cantidad de botellas que hay <strong>en</strong><br />

un tramo de cinta transportadora <strong>en</strong> una embotelladora. La cinta es alim<strong>en</strong>tada desde un almacén de botellas<br />

que ti<strong>en</strong>e un robot que increm<strong>en</strong>ta la variable Cantidad cada vez que coloca una botella <strong>en</strong> la cinta, mi<strong>en</strong>tras<br />

que dos robots ll<strong>en</strong>adores de cajas, decrem<strong>en</strong>tan <strong>en</strong> 12 la variable cantidad cada vez que toman 12 botellas de<br />

la cinta transportadora.<br />

Como es de suponer el robot despachador debe ll<strong>en</strong>ar la cinta más rápido de lo que los robots ll<strong>en</strong>adores la<br />

vacían.<br />

En la próxima <strong>en</strong>trega utilizaremos la sincronización para resolver el problema de que la cinta se quede vacía o<br />

se ll<strong>en</strong>e demasiado rápido. Como datos adicionales suponga que el robot despachador despacha una botella<br />

cada 250ms y que los robots ll<strong>en</strong>adores ll<strong>en</strong>an una caja cada 6 segundos. El robot despachador está conectado<br />

a RB0 y cada vez que pone una botella <strong>en</strong> la cinta transportadora le da un pulso al microcontrolador para<br />

indicárselo. Los robots ll<strong>en</strong>adores están conectados uno a RB1 y el otro a RB2 e igualm<strong>en</strong>te dan un pulso al<br />

microcontrolador cada vez que despachan una caja. La duración del pulso es de 100ms.<br />

Como es de suponer <strong>este</strong> problema lo podemos resolver de muchas maneras y lógicam<strong>en</strong>te sin el uso de los<br />

RTOS, pero eso se los dejo de tarea. Además el mecanismo de notificación de cada robot es un poco<br />

defici<strong>en</strong>te, pero ese no es el tema de <strong>este</strong> curso, aunque <strong>en</strong> su mom<strong>en</strong>to pondremos un ejemplo mejor al<br />

respecto, cuando rescatemos a las interrupciones del olvido.<br />

Analicemos un poco <strong>en</strong> detalle el problema: Supongamos que la tarea asociada al robot despachador comi<strong>en</strong>za<br />

a ejecutarse, lee el valor de la variable Cantidad, 100 botellas, y se va adormir esperando que el robot le<br />

notifique que ha puesto una botella <strong>en</strong> la cinta, <strong>en</strong> ese mom<strong>en</strong>to el RTOS le da permiso a la tarea del robot<br />

ll<strong>en</strong>ador 1 para que ejecute su código, ésta se da cu<strong>en</strong>ta que el robot ll<strong>en</strong>ó una caja por lo que lee la variable<br />

Cantidad y le resta 12 botellas, quedando Cantidad = 88 botellas. Ahora le toca de nuevo a la tarea del robot<br />

despachador ejecutarse y como ya se despachó una botella le suma uno al valor previam<strong>en</strong>te leído y actualiza<br />

la variable Cantidad, quedando 101 botellas, lo cual es falso.<br />

Este ejemplo puede mejorarse semánticam<strong>en</strong>te y evitarnos el uso de la sección crítica, todo ello gracias a que<br />

nuestro RTOS es cooperativo y <strong>en</strong> <strong>este</strong> caso, mi<strong>en</strong>tras la tarea esté ejecutándose ti<strong>en</strong>e todos los recursos para<br />

ella sola hasta que <strong>en</strong>tregue el procesador.<br />

Por lo tanto podemos escribir el código de modo que no haya ningún rtos_yield() intercalado con el código que<br />

accede a un recurso compartido, de esta forma la tarea se asegura el uso exclusivo del recurso compartido… a<br />

no ser que aparezcan las interrupciones <strong>en</strong> la palestra y las cosa se complique. Sin embargo <strong>en</strong> un sistema de<br />

tiempo compartido (los hay para PIC, ejemplo de ellos es el FreeRTOS), donde no sabemos cuando el RTOS<br />

nos quitará el procesador el uso de secciones críticas es OBLIGATORIO, para evitar problemas como los<br />

mostrados, y por eso es importante que dominemos esta técnica.<br />

Por otro lado el uso de Periféricos puede complicar más las cosas que el uso simple de la memoria y puede<br />

ocurrir que para ser efici<strong>en</strong>tes <strong>este</strong>mos obligados a poner un rtos_yield() d<strong>en</strong>tro del código de la sección<br />

crítica. Moraleja: apr<strong>en</strong>da a programar con secciones críticas o no programe con RTOS.<br />

El código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

#use RTOS(timer=0, minor_cycle=10ms)<br />

int8 semaphore; //Este es nuestro semáforo<br />

int16 iCantidad; //Esta es la cantidad de botellas <strong>en</strong> la <strong>este</strong>ra<br />

//constituye nuestro recurso compartido<br />

#task (rate=50ms, max=10ms)<br />

void R_Despachador();<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador1();


14 de 22<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador2();<br />

void main()<br />

{<br />

semaphore = 1; //Solo una tarea puede utilizar el recurso cada vez<br />

iCantidad = 100; //Inicializamos esta variable para t<strong>en</strong>er algunas botellas <strong>en</strong><br />

//la <strong>este</strong>ra.<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run();<br />

}<br />

void R_Despachador()<br />

{<br />

int Botellas;<br />

rtos_wait(semaphore); //Reclamamos el recurso y aquí comi<strong>en</strong>za la secc crítica<br />

Botellas = iCantidad; //Leemos la cantidad de botellas a una variable temporal<br />

if(input(PIN_B0)==1)<br />

{<br />

Botellas++; //Ya sabemos que <strong>este</strong> código no es efici<strong>en</strong>te pero<br />

iCantidad = Botellas; //sí es didáctico y por eso lo he utilizado así.<br />

}<br />

rtos_signal(semaphore); //Liberamos el semáforo y aquí se acaba la sec crítica<br />

rtos_yield(); // A dormir por otros 100ms para evitar poner dos veces la misma botella<br />

}<br />

void R_Ll<strong>en</strong>ador1()<br />

{<br />

rtos_wait(semaphore);<br />

if(input(PIN_B1)==1)<br />

iCantidad -= 12; //Este sí es un código lógico, pero <strong>en</strong>tonces el despachador<br />

//no nos daría problemas aunque nos vayamos a dormir d<strong>en</strong>tro de<br />

//la sección crítica.<br />

rtos_signal(semaphore);<br />

rtos_yield();<br />

}<br />

void R_Ll<strong>en</strong>ador2()<br />

{<br />

rtos_wait(semaphore);<br />

if(input(PIN_B2)==1)<br />

iCantidad -= 12;<br />

rtos_signal(semaphore);<br />

rtos_yield();<br />

}<br />

Este programa lo simulé <strong>en</strong> Proteus poni<strong>en</strong>do <strong>en</strong> RB0 una fu<strong>en</strong>te digital de tipo pattern con los sigui<strong>en</strong>tes<br />

parámetros:<br />

First Edge at (Secs)=1<br />

Desmarcar el check mark: Equal Mark/Space Timing?<br />

‘Mark’ Time (Secs) = 100m<br />

‘Sapce’ Time(Secs) = 150m<br />

Marcar el check mark: Continuos Secu<strong>en</strong>ce of Pulses<br />

Marcar el check mark: Standard Hig-Low Pulse Train<br />

Para RB1 y RB2 se utiliza una fu<strong>en</strong>te del mismo tipo con los sigui<strong>en</strong>tes parárametros cambiados<br />

Para RB1:<br />

First Edge at (Secs)=5<br />

‘Mark’ Time (Secs) = 100m<br />

‘Sapce’ Time(Secs) = 5.9<br />

Para RB2:<br />

First Edge at (Secs)=7<br />

‘Mark’ Time (Secs) = 100m


15 de 22<br />

‘Mark’ Time (Secs) = 100m<br />

‘Sapce’ Time(Secs) = 5.9<br />

Simul<strong>en</strong> y vean como cada vez que se pasa por rtos_wait() y si el semáforo es mayor que cero, se decrem<strong>en</strong>ta<br />

y se <strong>en</strong>tra <strong>en</strong> la sección crítica, si el semáforo es 0 <strong>en</strong>tonces la tarea espera a que esté disponible el recurso<br />

(semáforo>0) para ejecutar el código de la sección crítica.<br />

[Volver al Índice]<br />

> En sus marcas, listos, ¡FUERA!<br />

Un mom<strong>en</strong>to, un mom<strong>en</strong>to, no tan rápido que, hay una salida <strong>en</strong> falso del corredor del carril No. 2.<br />

En un caso como el anterior diríamos que ha fallado la sincronización de los corredores <strong>en</strong> el arranque, y la<br />

sincronización es el tema que vamos a tratar hoy <strong>en</strong> nuestro cursillo de los RTOS para uC.<br />

En el mundo de los uC es frecu<strong>en</strong>te la utilización de demoras para esperar a que ocurran ciertos hechos o<br />

ev<strong>en</strong>tos, para <strong>en</strong> consecu<strong>en</strong>cia hacer algo. La duración de estas demoras puede ser conocida, como <strong>en</strong> el caso<br />

de esperar a que una LCD coloque los datos <strong>en</strong> determinados registros antes de <strong>en</strong>viarle otro dato, o esperar a<br />

que la USART saque un dato que está transmiti<strong>en</strong>do antes de poder escribir nuevam<strong>en</strong>te <strong>en</strong> el registro de<br />

transmisión; sin embargo también son frecu<strong>en</strong>tes aquellas esperas <strong>en</strong> las que no sabemos cuanto podemos<br />

demorarnos.<br />

En los casos <strong>en</strong> que no conocemos cuanto debemos esperar se pued<strong>en</strong> utilizar las interrupciones, pero <strong>en</strong> los<br />

uC no t<strong>en</strong>emos interrupciones ilimitadas ni tampoco existe una biblioteca de mecanismos de interrupción<br />

disponibles para todos los casos que se nos pres<strong>en</strong>tan. Es por estas razones que muchas veces esperamos a la<br />

ocurr<strong>en</strong>cia de estos ev<strong>en</strong>tos haci<strong>en</strong>do suposiciones y blandi<strong>en</strong>do demoras.<br />

Para la implem<strong>en</strong>tación de demoras exist<strong>en</strong> varios mecanismos más o m<strong>en</strong>os efici<strong>en</strong>tes, sin embargo un SO no<br />

nos ofrece <strong>este</strong> tipo de mecanismos que podemos llamar un poco primitivos. Para la implem<strong>en</strong>tación de<br />

demoras efici<strong>en</strong>tes los SO han creado los mecanismos de sincronización de procesos.<br />

En la <strong>en</strong>trega anterior vimos la coordinación, <strong>en</strong> realidad los autores de los libros más reconocidos <strong>en</strong> el tema<br />

de los SO, han llamado a estos mecanismos y al que veremos hoy, “mecanismos de sincronización de<br />

procesos”, pero a mi me gusta distinguir <strong>en</strong>tre aquellos que se dedican especialm<strong>en</strong>te a implem<strong>en</strong>tar esperas<br />

efici<strong>en</strong>tes para la protección de recursos, de aquellos que se dedican a implem<strong>en</strong>tar esperas efici<strong>en</strong>tes para<br />

ev<strong>en</strong>tos por los cuales un proceso debe esperar antes de continuar su ejecución. Notemos que <strong>en</strong> ambos casos<br />

la tarea o proceso debe esperar, pero no <strong>en</strong> todos los casos la espera ti<strong>en</strong>e la misma naturaleza, <strong>en</strong> uno<br />

esperamos por un recurso físico al que queremos acceder y que no está disponible, <strong>en</strong> el otro esperamos a que<br />

se produzcan ciertos hechos que determinan el “estado” del sistema.<br />

Vamos a utilizar como refer<strong>en</strong>cia el problema anterior para ver el mecanismo de la sincronización <strong>en</strong> ejecución.<br />

Supongamos ahora que le hemos colocado a nuestros robots una <strong>en</strong>trada que cuando está <strong>en</strong> nivel bajo le<br />

indica al robot que no coloque o extraiga botellas de la cinta transportadora. Con <strong>este</strong> mecanismo simple<br />

vamos a tratar, por un lado, de evitar que el robot despachador de botellas ll<strong>en</strong>e demasiado la cinta y por el<br />

otro que los robots ll<strong>en</strong>adores de cajas trat<strong>en</strong> de ll<strong>en</strong>ar las cajas cuando no hay sufici<strong>en</strong>tes botellas <strong>en</strong> la cinta<br />

transportadora. Es decir, vamos sincronizar el proceso de ll<strong>en</strong>ado/vaciado de la cinta transportadora. Las<br />

v<strong>en</strong>tajas de esto son evid<strong>en</strong>tes, por ejemplo: si el robot ll<strong>en</strong>ador no ti<strong>en</strong>e botellas que poner <strong>en</strong> la cinta los<br />

robots ll<strong>en</strong>adores trabajarán hasta que la cantidad de botellas se lo permita; si los robots ll<strong>en</strong>adores dejan de<br />

trabajar, <strong>en</strong>tonces el robot despachador trabajará hasta que ll<strong>en</strong>e la cinta transportadora.<br />

Para lograr lo anterior, vamos a poner que la cantidad máxima de botellas que pued<strong>en</strong> estar <strong>en</strong> la cinta es de<br />

100 y que la cantidad mínima de botellas es 24, por lo que cada una de las tareas que ati<strong>en</strong>de a los robots<br />

deberá, además de llevar la actualización del total de botellas <strong>en</strong> la cinta, indicarle a los robots que se<br />

det<strong>en</strong>gan cuando la cantidad de botellas <strong>en</strong> la cinta esté fuera del rango especificado. Además vamos a utilizar<br />

el puerto serie para Tx la cantidad de botellas que hay <strong>en</strong> la cinta <strong>en</strong> cada mom<strong>en</strong>to.<br />

Las <strong>en</strong>tradas a los robots (salidas de nuestro PIC) las hemos colocado <strong>en</strong> los pines RB3..RB5, <strong>en</strong> RB3 al robot<br />

despachador, <strong>en</strong> RB4 al robot ll<strong>en</strong>ador1 y <strong>en</strong> RB5 al robot ll<strong>en</strong>ador2<br />

Para hacer la sincronización de tareas el RTOS de CCS nos ofrece una sola función, aunque parezca poco esta<br />

función es bi<strong>en</strong> poderosa, vamos a verla con más det<strong>en</strong>imi<strong>en</strong>to. La función se llama rtos_await(expr) lo que<br />

ti<strong>en</strong>es que pasarle es una expresión lógica que la función evaluará, si esta resulta verdadera <strong>en</strong>tonces<br />

continúas la ejecución normal de tu programa, si te devuelve falso, <strong>en</strong>tonces rtos_await() le cederá el<br />

procesador al RTOS y la tarea quedará bloqueada hasta que exp se cumpla para <strong>en</strong>tonces continuar <strong>en</strong> la línea<br />

sigui<strong>en</strong>te a la llamada a la función.<br />

Ahora bi<strong>en</strong>, es cierto que esta función es poderosa y simple, pero eso implica que t<strong>en</strong>emos que saber donde<br />

ponerla, ya que si no evaluamos bi<strong>en</strong> nuestras expresiones o no colocamos la función <strong>en</strong> el lugar adecuado,<br />

estaremos sometiéndonos a riesgos durante la ejecución de nuestro código. Por ejemplo puede ocurrir que


16 de 22<br />

estaremos sometiéndonos a riesgos durante la ejecución de nuestro código. Por ejemplo puede ocurrir que<br />

alguna tarea se quede bloqueada esperando por siempre a que la expresión lógica se evalúe de verdadera y<br />

esto nunca ocurra o que la expresión esté mal diseñada y la tarea no se bloquee cuando haga falta. Bi<strong>en</strong> todos<br />

esos problemas los veremos más adelante cuando la señorita Némesis (diosa griega de la v<strong>en</strong>ganza y la<br />

fortuna) haga acto de pres<strong>en</strong>cia para echarnos a perder toda la dicha que los RTOS tra<strong>en</strong> al mundo de la<br />

programación con uC.<br />

Desde aqui puedes descargar el código fu<strong>en</strong>te y la simulación con Proteus (57Kb, <strong>formato</strong> ZIP)<br />

Cuando corran el ejemplo not<strong>en</strong> como la cantidad de botellas <strong>en</strong> la cinta va desc<strong>en</strong>di<strong>en</strong>do hasta que al llegar a<br />

las 24 botellas, después la cantidad de botellas <strong>en</strong> la cinta se mant<strong>en</strong>drá más o m<strong>en</strong>os sobre las 20 y nunca<br />

por debajo de 12. Las compuertas AND de conjunto con las formas de onda hac<strong>en</strong> la función de los robots.<br />

El código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

#use RTOS(timer=0, minor_cycle=10ms)<br />

int8 semaphore; //Este es nuestro semáforo<br />

int16 iCantidad; //Esta es la cantidad de botellas <strong>en</strong> la <strong>este</strong>ra<br />

//constituye nuestro recurso compartido<br />

#task (rate=50ms, max=10ms)<br />

void R_Despachador();<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador1();<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador2();<br />

void main()<br />

{<br />

semaphore = 1; //Solo una tarea puede utilizar el recurso cada vez<br />

iCantidad = 120; //Inicializamos esta variable para t<strong>en</strong>er algunas botellas <strong>en</strong><br />

//la <strong>este</strong>ra, normalm<strong>en</strong>te deberiamos t<strong>en</strong>er un s<strong>en</strong>sor que nos reporte<br />

//<strong>en</strong> algun mom<strong>en</strong>to el total de botellas <strong>en</strong> la cinta, ya que un<br />

//robot revisor de ll<strong>en</strong>ado o una persona puede retirar botellas<br />

//de la cinta<br />

//Al com<strong>en</strong>zar todos los robots estan deshabilitados<br />

output_bit( PIN_B3, 0);<br />

output_bit( PIN_B4, 0);<br />

output_bit( PIN_B5, 0);<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run();<br />

}<br />

void R_Despachador()<br />

{<br />

//Note como hacemos la sincronizacion fuera de la seccion critica, mas adelante veremos<br />

//que esto no siempre es posible hacerlo o que las cosas se complican un poco mas<br />

//de lo que hemos visto hasta ahora.<br />

rtos_await(iCantidad= 100)<br />

output_bit( PIN_B3, 0); //Le decimos al robot despachador que no ponga mas botellas<br />

rtos_signal(semaphore); //Liberamos el semáforo y aquí se acaba la sec crítica<br />

printf("%3.0w \r",iCantidad);<br />

rtos_yield(); // A dormir por otros 50ms para evitar poner dos veces la misma botella<br />

}


17 de 22<br />

void R_Ll<strong>en</strong>ador1()<br />

{<br />

//El robot debe esperar a que la cinta t<strong>en</strong>ga sufici<strong>en</strong>tes botellas para sacar antes<br />

//de com<strong>en</strong>zar a trabajar.<br />

rtos_await(iCantidad>24); //Esperemos a que se ll<strong>en</strong>e un poco la cinta<br />

output_bit( PIN_B4, 1); //A partir de aqui, si no se podia antes, sacar botellas<br />

rtos_wait(semaphore);<br />

if(input(PIN_B1)==1)<br />

iCantidad -= 12;<br />

if(iCantidad 24); //Esperemos a que se ll<strong>en</strong>e un poco la cinta<br />

output_bit( PIN_B5, 1); //A partir de aqui, si no se podia antes, sacar botellas<br />

rtos_wait(semaphore);<br />

if(input(PIN_B2)==1)<br />

iCantidad -= 12;<br />

if(iCantidad RTOS mail<br />

Hasta el mom<strong>en</strong>to solam<strong>en</strong>te hemos visto mecanismos que nos permit<strong>en</strong> simplificar el diseño de nuestros<br />

programas, pero hoy vamos a ver una nueva pot<strong>en</strong>cialidad de los RTOS que es una cuestión realm<strong>en</strong>te<br />

novedosa <strong>en</strong> cuanto la visión de la programación para uC a la cual estamos acostumbrados.<br />

Cuando hacemos una llamada a una función, es frecu<strong>en</strong>te pasarle algún parámetro para que esta pueda hacer<br />

su tarea, mi<strong>en</strong>tras la función trabaja, nuestro programa espera paci<strong>en</strong>tem<strong>en</strong>te a que la función retorne y nos<br />

devuelva el resultado, que puede ser un valor de retorno, un arreglo cambiado o simplem<strong>en</strong>te el cambio <strong>en</strong> el<br />

estado de algún periférico o salidas del uC.<br />

El párrafo anterior describe lo que hace nuestro programa cuando llamamos a una función, sin embargo nunca<br />

hemos visto que una función le <strong>en</strong>víe un dato a otra (no <strong>en</strong> la llamada a la función, sino fuera de ésta) para<br />

que cuando le toque ejecutarse tome esos valores y los procese, y si hay que devolver algún resultado<br />

<strong>en</strong>tonces que nos <strong>en</strong>víe un acuse de recibo. Es lógico que un mecanismo como el que acabo de describir no se<br />

utilice <strong>en</strong> las técnicas de programación anterior porque la ejecución secu<strong>en</strong>cial del código presupone que no se


18 de 22<br />

utilice <strong>en</strong> las técnicas de programación anterior porque la ejecución secu<strong>en</strong>cial del código presupone que no se<br />

requiera de un mecanismo como <strong>este</strong>.<br />

Lo más cercano al método descrito <strong>en</strong> el poner datos <strong>en</strong> algún lugar para que una función lo procese es el uso<br />

de las interrupciones, éstas deb<strong>en</strong> procesar rápidam<strong>en</strong>te el ev<strong>en</strong>to de interrupción y pude que pongamos el<br />

dato <strong>en</strong> algún lugar y levantemos una bandera para que cuando a la función <strong>en</strong>cargada de procesar los datos<br />

le toque ejecutarse lea la bandera, procese los datos y coloque la bandera <strong>en</strong> el estado que notifica que ya se<br />

procesó, no para notificar a otra función sino para notificárselo a ella misma, no vaya a ser que cuando le<br />

toque ejecutarse nuevam<strong>en</strong>te procese los mismos resultados nuevam<strong>en</strong>te o haga algo indebido.<br />

Un ejemplo de lo anterior puede se el uso del conversor AD, <strong>en</strong> algún lugar del programa lo mandamos a<br />

convertir; una vez hecha la conversión se produce una interrupción que es at<strong>en</strong>dida <strong>en</strong> la correspondi<strong>en</strong>te<br />

subrutina de at<strong>en</strong>ción a esa interrupción; leemos el dato lo ponemos <strong>en</strong> una variable e increm<strong>en</strong>tamos un<br />

contador. Posteriorm<strong>en</strong>te le toca ejecutarse a la función que promedia los resultados, ésta comprueba si hay,<br />

digamos 200 muestras, si las hay hace el cálculo que pone <strong>en</strong> otra variable y deja el contador <strong>en</strong> cero. Este<br />

mecanismo es eficaz porque se ha utilizado durante mucho tiempo, pero los RTOS brindan una solución<br />

elegante para hacer esto <strong>en</strong> el contexto de la ejecución de tareas.<br />

Estos mecanismos pued<strong>en</strong> funcionar también <strong>en</strong>tre funciones pero t<strong>en</strong>dremos el problema de tratar con un montón de<br />

estructuras de datos, banderas y contadores, complejas expresiones lógicas a procesar… ¿se acuerdan de eso?<br />

Ahora imagín<strong>en</strong>se hacer todo lo anterior cuando, <strong>en</strong> vez de llamadas a funciones metidas d<strong>en</strong>tro de un código<br />

que se ejecuta más o m<strong>en</strong>os estructuradam<strong>en</strong>te, lo que t<strong>en</strong>emos es unas cuantas tareas de las que no<br />

t<strong>en</strong>emos un control de cuando ni como se ejecutarán. Realm<strong>en</strong>te puede ser una verdadera pesadilla hacer un<br />

programa productivo, y es por ello que los RTOS nos ofrec<strong>en</strong> un poderoso mecanismo para hacer eso, y como<br />

siempre, <strong>este</strong> mecanismo también es relativam<strong>en</strong>te simple.<br />

Para hacer lo anterior los RTOS implem<strong>en</strong>tan un mecanismo de m<strong>en</strong>sajes. Sí, amigos míos, un RTOS<br />

implem<strong>en</strong>ta una función similar a la de los correos.<br />

El funcionami<strong>en</strong>to de ese mecanismo es simple, cuando una tarea o subrutina de at<strong>en</strong>ción a interrupciones<br />

necesita notificarle algo a otra tarea llama a una función que pone el dato <strong>en</strong> la cola de la tarea <strong>en</strong> cuestión,<br />

cuando a la tarea que recibió el m<strong>en</strong>saje le toca ejecutarse debe, <strong>en</strong> algún lugar consultar su cola de<br />

m<strong>en</strong>sajes, si hay m<strong>en</strong>sajes debe leerlos y procesarlos, como pued<strong>en</strong> ver <strong>este</strong> mecanismo es bastante parecido<br />

a lo que hacemos habitualm<strong>en</strong>te.<br />

Si yo quiero pasarles un m<strong>en</strong>saje, por ejemplo un post, hago lo sigui<strong>en</strong>te:<br />

- lo escribo, <strong>este</strong> es el equival<strong>en</strong>te a realizar una tarea<br />

- lo mando al foro, <strong>este</strong> es el equival<strong>en</strong>te a ponerlo <strong>en</strong> la cola de m<strong>en</strong>sajes del hilo sobre RTOS<br />

- ustedes llegan y consultan si hay m<strong>en</strong>sajes nuevos <strong>en</strong> el hilo, por supuesto llegan cuando su tarea de leer el<br />

foro está activa<br />

- si hay un m<strong>en</strong>saje nuevo, normalm<strong>en</strong>te tratarán de leerlo y poner <strong>en</strong> práctica los nuevos conocimi<strong>en</strong>tos<br />

- si se si<strong>en</strong>t<strong>en</strong> impresionados, me escribirán un m<strong>en</strong>saje a mi correo privado, dándome un acuse de recibo (no<br />

hagan esto si no es estrictam<strong>en</strong>te necesario, no vaya a ser que me vuelvan loco)<br />

Aquí se ha puesto de manifiesto un ejemplo del sistema de m<strong>en</strong>sajes más simple utilizado por un SO: elaborar<br />

y <strong>en</strong>viar de una parte y consultar si hay un m<strong>en</strong>saje, procesar y <strong>en</strong>viar acuse de recibo si es necesario de la<br />

otra parte.<br />

(Todo esto está muy bi<strong>en</strong>. Pero yo quiero código y ejemplos de verdad.)<br />

Vale, no nos ofusquemos, primero <strong>en</strong>t<strong>en</strong>der bi<strong>en</strong> el concepto, luego ponerlo <strong>en</strong> práctica.<br />

Lo primero que t<strong>en</strong>emos que hacer es decirle al compilador que le cree una cola de m<strong>en</strong>sajes a aquella tarea a<br />

la cual queremos pasarle m<strong>en</strong>sajes, para eso t<strong>en</strong>emos el parámetro queue, d<strong>en</strong>tro de la directiva #task, con<br />

<strong>este</strong> parámetro le indicamos al RTOS que reserve memoria y cree una cola de m<strong>en</strong>sajes para la tarea, la<br />

declaración de una tarea con cola de m<strong>en</strong>sajes sería como sigue:<br />

El código:<br />

#task(rate = 1s, max=20ms, queue=5)<br />

void Tarea1(void);<br />

En la declaración de la tarea el parámetro queue = 5, le dice al compilador que cree una cola de 5 bytes para<br />

la Tarea1.<br />

Para el <strong>en</strong>vío y recepción de m<strong>en</strong>sajes t<strong>en</strong>emos las sigui<strong>en</strong>tes funciones:<br />

RTOS_MSG_SEND( )<br />

RTOS_MSG_POLL( )<br />

RTOS_MSG_READ( )


19 de 22<br />

RTOS_MSG_READ( )<br />

RTOS_MSG_SEND(task, byte); permite <strong>en</strong>viar un byte de datos a la cola de m<strong>en</strong>sajes de una tarea. Esta es la<br />

única función del RTOS de CCS que puede llamarse desde fuera de una tarea, lo que permite que desde una<br />

subrutina de at<strong>en</strong>ción a interrupción se le <strong>en</strong>ví<strong>en</strong> m<strong>en</strong>sajes a una tarea. El parámetro task es para el nombre<br />

de la tarea y byte es un dato de 8 bits (un char o un int8), así que si queremos <strong>en</strong>viar un float o una<br />

estructura t<strong>en</strong>dremos que descomponer antes ese dato <strong>en</strong> bytes y luego componerlos cuando la cola de<br />

m<strong>en</strong>sajes sea leída.<br />

int RTOS_MSG_POLL(); es la función que permite a una tarea conocer si ti<strong>en</strong>e m<strong>en</strong>sajes <strong>en</strong> su cola de<br />

m<strong>en</strong>sajes, no se puede llamar desde otra tarea para conocer cuantos m<strong>en</strong>sajes ti<strong>en</strong>e la tarea fulana. Devuelve<br />

<strong>en</strong> un <strong>en</strong>tero la cantidad de bytes ocupados d<strong>en</strong>tro de la cola de m<strong>en</strong>sajes.<br />

int8 RTOS_MSG_READ(); permite leer un byte de la cola de m<strong>en</strong>sajes. Cuando se ejecuta esta función se lee el<br />

byte y se saca de la cola, por lo que si el dato se pierde no se podrá recuperar, si se llama a la función y no<br />

hay m<strong>en</strong>sajes <strong>en</strong> la cola se pued<strong>en</strong> obt<strong>en</strong>er datos falsos.<br />

En el ejemplo que hemos estado vi<strong>en</strong>do sobre la embotelladora, vamos a incluir un servicio especial para<br />

balancear la cantidad de botellas que hay d<strong>en</strong>tro de la cinta transportadora, para ello pondremos una tarea<br />

adicional que recibirá de las otras tareas el estado de la cantidad de botellas d<strong>en</strong>tro de la cinta. Si la cinta se<br />

está vaciando demasiado rápido, esta tarea se <strong>en</strong>cargará de inhabilitar los robots ll<strong>en</strong>adores de cajas, si se<br />

está ll<strong>en</strong>ando muy rápido pues <strong>en</strong>tonces se deshabilita al robot que despacha botellas hacia la cinta.<br />

Para lograr esto cada vez que un robot ejecuta la tarea de ll<strong>en</strong>ado de cajas o despacho de botellas le notifica a<br />

la tarea reguladora la cantidad de botellas que hay <strong>en</strong> la cinta, con <strong>este</strong> mecanismo evitamos que la tarea<br />

supervisora t<strong>en</strong>ga que leer el recurso compartido iCantidad, y que t<strong>en</strong>ga que sincronizarse con los robots. La<br />

cola de m<strong>en</strong>sajes t<strong>en</strong>drá dos bytes (aunque solo se necesita uno, después explico por que hac<strong>en</strong> falta dos)<br />

donde se reflejará la última escritura realizada por cualquiera de los tres robots. Además, delegaremos <strong>en</strong> esta<br />

tarea la transmisión de la cantidad de botellas que hay <strong>en</strong> la cinta. La cantidad media de botellas a t<strong>en</strong>er <strong>en</strong> la<br />

cinta es 50.<br />

El código:<br />

#include "D:\Docum<strong>en</strong>tos\Projects\RTOS\RTOS.h"<br />

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)<br />

#use RTOS(timer=0, minor_cycle=10ms)<br />

int8 semaphore; //Este es nuestro semáforo<br />

int8 iCantidad; //Esta es la cantidad de botellas <strong>en</strong> la <strong>este</strong>ra<br />

//constituye nuestro recurso compartido<br />

#task (rate=50ms, max=10ms)<br />

void R_Despachador();<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador1();<br />

#task (rate=50ms, max=10ms)<br />

void R_Ll<strong>en</strong>ador2();<br />

#task (rate=1s, max=10ms, queue=2) //la cola ti<strong>en</strong>e 2 byte aunque solam<strong>en</strong>te necesitamos 1<br />

void Supervisor();<br />

void main()<br />

{<br />

semaphore = 1; //Solo una tarea puede utilizar el recurso cada vez<br />

iCantidad = 120; //Inicializamos esta variable para t<strong>en</strong>er algunas botellas <strong>en</strong><br />

//la <strong>este</strong>ra, normalm<strong>en</strong>te deberiamos t<strong>en</strong>er un s<strong>en</strong>sor que nos reporte<br />

//<strong>en</strong> algun mom<strong>en</strong>to el total de botellas <strong>en</strong> la cinta, ya que un<br />

//robot revisor de ll<strong>en</strong>ado o una persona puede retirar botellas<br />

//de la cinta<br />

//Al com<strong>en</strong>zar todos los robots estan deshabilitados<br />

output_bit( PIN_B3, 0);<br />

output_bit( PIN_B4, 0);<br />

output_bit( PIN_B5, 0);<br />

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);<br />

rtos_run();<br />

}<br />

void R_Despachador()


20 de 22<br />

void R_Despachador()<br />

{<br />

//Note como hacemos la sincronizacion fuera de la seccion critica, mas adelante veremos<br />

//que esto no siempre es posible hacerlo o que las cosas se complican un poco mas<br />

//de lo que hemos visto hasta ahora.<br />

rtos_await(iCantidad= 100)<br />

output_bit( PIN_B3, 0); //Le decimos al robot despachador que no ponga mas botellas<br />

rtos_msg_s<strong>en</strong>d(Supervisor, iCantidad); //Enviamos un m<strong>en</strong>saje con la cant de botellas<br />

rtos_signal(semaphore); //Liberamos el semáforo y aquí se acaba la sec crítica<br />

rtos_yield(); // A dormir por otros 50ms para evitar poner dos veces la misma botella<br />

}<br />

void R_Ll<strong>en</strong>ador1()<br />

{<br />

//El robot debe esperar a que la cinta t<strong>en</strong>ga sufici<strong>en</strong>tes botellas para sacar antes<br />

//de com<strong>en</strong>zar a trabajar.<br />

rtos_await(iCantidad>24); //Esperemos a que se ll<strong>en</strong>e un poco la cinta<br />

output_bit( PIN_B4, 1); //A partir de aqui, si no se podia antes, sacar botellas<br />

rtos_wait(semaphore);<br />

if(input(PIN_B1)==1)<br />

iCantidad -= 12;<br />

if(iCantidad 24); //Esperemos a que se ll<strong>en</strong>e un poco la cinta<br />

output_bit( PIN_B5, 1); //A partir de aqui, si no se podia antes, sacar botellas<br />

rtos_wait(semaphore);<br />

if(input(PIN_B2)==1)<br />

iCantidad -= 12;<br />

if(iCantidad 0); //Esperamos a que haya algun m<strong>en</strong>saje <strong>en</strong> la cola<br />

iBotellas = rtos_msg_read(); //Leemos el m<strong>en</strong>saje


21 de 22<br />

iBotellas = rtos_msg_read(); //Leemos el m<strong>en</strong>saje<br />

//Lo que hacemos ahora es comprobar la cantidad de botellas que hay <strong>en</strong> la <strong>este</strong>ra<br />

//y <strong>en</strong> funcion de eso habilitamos y deshabilitamos las tareas y los robots que hac<strong>en</strong> falta<br />

//para controlar la cantidad de botellas <strong>en</strong> la <strong>este</strong>ra<br />

if(iBotellas > 50)<br />

{<br />

output_bit( PIN_B3, 0); //No despachar mas botellas<br />

rtos_disable(R_Despachador);<br />

rtos_<strong>en</strong>able(R_Ll<strong>en</strong>ador1);<br />

rtos_<strong>en</strong>able(R_Ll<strong>en</strong>ador2);<br />

}<br />

else<br />

{<br />

rtos_<strong>en</strong>able(R_Despachador); //No ll<strong>en</strong>ar mas cajas<br />

output_bit( PIN_B4, 0);<br />

rtos_disable(R_Ll<strong>en</strong>ador1);<br />

output_bit( PIN_B5, 0);<br />

rtos_disable(R_Ll<strong>en</strong>ador2);<br />

}<br />

printf("%3.0w \r",iBotellas); //Transmitir la cantidad de botellas<br />

}<br />

Este programa se puede simular con el mismo fichero de Proteus que publiqué antes, not<strong>en</strong> como la cantidad<br />

de botellas <strong>en</strong> la cinta transportadora se manti<strong>en</strong>e sobre las 50 botellas. En el caso de la cola de m<strong>en</strong>sajes<br />

deb<strong>en</strong> especificar n+1 bytes de los que necesit<strong>en</strong>, la razón no la conozco pero con 1 byte no funciona, debe ser<br />

algún problema de la implem<strong>en</strong>tación de CCS o alguna limitación de los PIC que obliga a ello.<br />

Bu<strong>en</strong>o espero que les sirva.<br />

PD: Lo de esta embotelladora es un inv<strong>en</strong>to, <strong>en</strong> ningún mom<strong>en</strong>to esto se ha probado <strong>en</strong> una planta de ese<br />

tipo.<br />

El tiempo que utilicé para <strong>este</strong> ejemplo fue de 3:30 horas, incluy<strong>en</strong>do la redacción y programación.<br />

[Volver al Índice]<br />

> Datos de Contacto:<br />

Los datos de contacto de Reinier son los sigui<strong>en</strong>tes:<br />

e-mail: reiniertl@gmail.com<br />

Ciudad de La Habana, Cuba<br />

WEB provisional: http://reinier-torres-labrada.neurona.com<br />

[Volver al Índice]<br />

> Bibliografía:<br />

- Sistemas Operativos Diseño e Implem<strong>en</strong>tación. Andrew S. Tan<strong>en</strong>baum.<br />

[Volver al Índice]<br />

En construcción...perman<strong>en</strong>tem<strong>en</strong>te.<br />

Pasa <strong>en</strong> unos días para ver las novedades.


22 de 22<br />

Pasa <strong>en</strong> unos días para ver las novedades.<br />

www.ucontrol.com.ar | Desarrollo de sistemas de automatización y control | Pehuajó - Bu<strong>en</strong>os Aires - Arg<strong>en</strong>tina<br />

e-mail: arielpalazzesi@gmail.com

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!