Descarga este documento en formato PDF - Ucontrol
Descarga este documento en formato PDF - Ucontrol
Descarga este documento en formato PDF - Ucontrol
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