lunes, 14 de abril de 2014

FreeRTOS (Completo)

1.-Preparando el ambiente: como crear un proyecto con FreeRtos.

Bajo PIC18F (C18)

Cumpliendo con la tarea que prometimos que era utilizar un RTOS que este disponible sobre un gran numero de microcontroladores, iniciamos el curso con un tutorial paso a paso para crear un proyecto con FREERTOS para PIC18, ya que es el microcontrolador de mayor llegada para mucha gente. Luego una vez madurado pasaremos a una arquitectura ARM sobre el mismo RTOS de manera de aprovechar todo lo hecho o por hacer. Armar el ambiente de trabajo para tu RTOS puede que no sea una tarea simple la primera vez, ya que hay que tener en cuenta muchas cosas que son solo dependientes de la arquitectura utilizada, asi que aqui te daremos una mano para pasarlo lo mas pragmatico y rapidamente posible.
Las imagenes son orientativas, no se incluiran todas las posibles.

  • 1.Crear un proyecto con "proyect Wizard" eligiendo el PIC18F a utilizar
  • 2.Elegir el C18 como compilador C ANSI 89.
  • 3.Se deben agregar al proyecto bajo "sources" las fuentes del mismo
        list.c
        queue.c
        task.c
        Si se va a utilizar corutinas ->croutine.c
  • 4.Deben agregar al proyecto bajo "portable"
        a. heap_x.c donde x es 1,2 o 3 dependiendo del modelo de memoria en \MemMang
           1 es un modelo de memoria estatico, una vez tomada la memoria no se puede devolver.
           2 es un modelo dinamico pero sin reodenamiento.
           3 es un modelo completo de manejo de memoria.
        b. port.c dependiente del dispositivo que en este caso es \MPLAB\PIC18F
  • 5.Se debe agregar el archivo linker dpte del micro modificado para disponer de un espacio de heap lineal mas grande, puedes comparar el original de microchip con el provisto para entender como se modifica.
  • 6.En opciones del compilador, se debe configurar:
        a.Bajo categoria general, en Nivel de diagnostico poner "Errors, warnings and messages" que es similar a la directiva -w3
        b.Bajo Memory model, usar stack model Multi bank (directiva -Ls)
        c.Bajo Optimization seleccionar custom y deshabilitar procedural abstraction (directiva -Opa-)
        d.bajo macro definition del C18 se debe agregar las siguientes directivas:
        MPLAB_PIC18F_PORT
        -nw 2074 -nw 2066
        Tiene que quedar la directiva total en "-w3 -DMPLAB_PIC18F_PORT -D-nw 2074 -nw 2066 -Ls -Opa-"
  • 7.Debes agregar los path a las fuentes del FreeRTOS(\INCLUDE) en el "include search path" del C18.
       Aunque tiene que ver con la instalacion del C18, debes tenes en el proyecto los path a los includes del C18 y a las librerias.
  • 8.Se debe agregar el archivo FreeRTOSConfig.h con los parametros de configuracion del sistema operativo, puedes usar como ejemplo el provisto aqui.
  • 9.Debes agregar tu archivo con el main con la configuracion de arranque ejecucion de tus tareas y del Scheduler.
Nota importante: Para que compile este ejemplo debes comentar en port.c las llamadas a vSerialRxISR y vSerialTxISR en el servicio de interrupciones ya que el ejemplo no agrega el modulo de serial.

El ejemplo tiene paths a los archivos en duro osea que deben ser modificados por los path de donde se encuentra tu compilador, tu MPLAB y el FreeRTOS.
Se pueden hacer path relativos al path actual de tu proyecto, eso es si tu ubicas tu proyecto dentro del arbol de archivos de FreeRtos.

Como nota adicional la unica tarea cargada no hace mas que demorar 10 ticks y volverse a dormir...
No era el objetivo de este post mostrar las capacidades del RTOS, eso queda para otros post.
Agregado:
Para poder manterner path a los archivos de tu proyecto y del source, es preferible ubicar tu proyecto dentro del arbol de directorios de FreeRTOS.
Yo cree una carpeta al mismo nivel que los demos de manera de utilizar path relativos y hacerlo mas flexible en su adaptacion.


\Demo
\License
\Projects
<--Carpeta agregada
\Source
\TraceCon

Nota Importante: manejo del port
Es importante remarcar ciertos aspectos del uso del RTOS FreeRTOS con el compilador C18.
Este compilador como todos los que manejan arquitecturas de 8 bits limitadas, aboga por la eficiencia en velocidad, tamaño de codigo de programa y uso de memoria RAM.
Por ende algunas limitaciones tiene:
Aunque el modelo permite reentrancia, no la realiza a traves de manejo de memoria en el stack sino que genera una estructura de datos adicional estatica, es mas rapido pero escapa del estandar.
Ademas para mantener la integridad en calculos matematicos en variables atomizables (por ejemplo enteros en 32 bits que llegan 4 bytes) maneja en una posicion fija buen numero de posiciones de memoria.

Con respecto al FREERTOS, este requiere una gran area de memoria lineal para manejar su queue (cola), el problema es que el C18 maneja bancos de 256 bytes por lo que se debe modificar el archivo linker script para unir varias de estas secciones, pero lo importante es NO UNIRLAS TODAS ya que el compilador todavia necesita utilizar algunas de estas secciones para sus propositos.
El tamaño de esta queue se declara en el archivo FreeRTOSConfig.h y se llama configTOTAL_HEAP_SIZE.

Por ende las siguientes modificaciones al modelo son requeridas:
  • Modificar el archivo lkr (EJ: 18f2620.lkr) uniendo varias secciones de ram en una sola (pero no todas)
  • Modificar en el port para C18 (port.c) el numero de datos usados por matematica que se deben guardar en el stack.
    Para esto se debe mirar el archivo .map que genera en la misma carpeta de proyecto y contar cuantos bytes
    consume MATH_DATA y .tmpdata juntas este numero menos uno es el valor a poner en portCOMPILER_MANAGED_MEMORY_SIZE.
  • Agregar tantas lineas de :
    MOVFF    POSTINC0, PREINC1
    y
    MOVFF    POSTDEC1, POSTDEC0
    en la parte de salvar y restaurar contexto respectivamente, como el numero que tenemos en portCOMPILER_MANAGED_MEMORY_SIZE.

 AT91SAM7S 

De la misma manera que se realizo para el Tutorial paso a paso para crear un proyecto con FreeRtos para PIC18F en este caso pasamos a un microcontrolador mas importante como el AT91SAM7S.
En este caso simplemente recomiendo utilizar el cascaron que brindo aqui ya que es funcional y de aqui solo hay que agregar las fuentes para cada caso especifico. Una cosa a dejar en claro es: porque si todos los micros basados en ARM tienen la misma arquitectura, los ports (adaptaciones para el RTOS), son diferentes? Esto se debe a que aunque la arquitectura del procesador sea la misma, difieren las implementaciones de los perifericos (timers que son usados por el RTOS para el context switch, sistema de interrupciones, etc) y tambien es totalmente diferente el tipo de compilador usado por cada IDE (KEIL, IAR, GCC, etc) con lo que cada uno requerira un tunning especifico.

En el caso de KEIL no posee un port avalado por FREERTOS para la linea SAM7S con lo que utilizamos uno NO AVALADO pero totalmente funcional.
Es en realidad para un AT91SAM7X pero es exactamente lo mismo ya que las diferencias la maneja la libreria de KEIL.

Lo que si hay que agregar en el header file portable.h ubicado en FreeRTOS\Source\include\ la siguiente linea:
Código:
#ifdef KEIL_SAM7
 #include "..\..\Source\portable\Keil\SAM7\portmacro.h"
#endif
Igual agrego el archivo para mayor conveniencia.

Con esto hariamos uso al port de SAM7 para KEIL que debemos ubicar en el path del include (./Source/portable/Keil/SAM7/).
Estas librerias son:
  • portISR.c
  • portasm.s
  • port.c

De esta manera le de decimos al FREERTOS que si tiene soporte para el sistema en cuestion.

A continuacion muestro una imagen del espacio de trabajo:


Tenemos las fuentes particulares del proyecto (en este caso solo main.c y el assembler de arranque), el codigo del port especifico para este micro y las funciones propias del RTOS. Si desearamos modificar opciones del build debemos utilizar el modulo de opciones de build: El que tiene la varita es el de "options for target", aqui modificamos el tipo de micro a usar, las posiciones de memoria a usar, el tipo de debug, los path al codigo C/C++ y ASM, etc.El que tiene 3 cajitas de colores permite sacar o agregar modulos c/c++/h/asm al proyecto, ademas de documentacion. La tarea de prueba levanta 4 instancias de la tarea blinkear led y cada una captura 1 de los 4 leds disponibles de la placa de desarrollo de ATMEL.
El archivo zip tiene la carpeta raiz del proyecto con lo que no se van a perder para instalarlo en el directorio de FreeRTOS.

 ARM7_LPC2106_Keil_GCC

Hola gente del Foro, siguiendo mi post anterior ARM Toolchains y luego de mucho peludear por la Web para hacer compilar primero una demo para ARM-GCC pero desde la IDE de Keil (uVision), logré, luego después de más peludeo, hacer compilar uno de los ports de FreeRTOS "ARM7_LPC2106_GCC" (o sea un port de FreeRTOS para NXP LPC2106 usando el GNU C Compiler [es decir GCC]) usando, al igual que antes, el Keil uVision como IDE. A este nuevo toolchain lo llamé "ARM7_LPC2106_Keil_GCC" el cual adjunto. Para configurar el Keil uVision me ayudé mucho con otro port de FreeRTOS pero para el Keil "ARM7_LPC2129_Keil_RVDS", con el compilador que viene junto con él, es decir el RVDS (RealView Development Suite).
Por qué Keil? bueno porque, como expliqué en el post anterior, es el UNICO que soporta el programador que ya tengo (ULink), por otro lado si bien no lo puedo asegurar, toda la parte de debug y simulación debe de estar muy bien soportada teniendo en cuenta que Keil forma parte de ARM desde hace un tiempo.
Para qué armé este Toolchain? Bueno, siempre fui partidario de usar plataformas abiertas y además de un modo u otro GNU y FreeRTOS de un modo u otro llegan a todos lados.
Qué hay que tener en cuenta al armar un Toolchain como este? Como puse mas arriba el Toolchain que armé es la suma del source/makefile del port "ARM7_LPC2106_GCC" mirando al mismo tiempo la configuración del IDE del uVision en el port "ARM7_LPC2129_Keil_RVDS". En principio lo que se hace es armar las carpetas del port para GCC copiando solo Sources(.c), Headers(.h), Startup Code(.s) y Linker Script(.ld), y luego crear el proyecto con el Keil ya configurado para compilar con GCC para luego traspasar las directivas del precompilador y los path a las librerías que se pueden sacar del makefile. Si no entienden cómo funciona el makefile lo que pueden hacer es compilarlo (aparte en una carpeta específica para GCC) primero con el GCC, es decir escribir 'make'. Esto es muy importante porque antes de hacer funcionar el GCC con el IDE del Keil sería bueno asegurarse que funciona solito, es decir, como fue concebido. En mi caso, para 'robar' los parámetros que el makefile le pasa al compilador y al linker usé un viejo truco de DOS (ahora 'Simbolo de sistema' en Windows) que es enviar la salida por defecto (CON) de cualquier aplicación de consola a un archivo de esta forma: "make > make_out.txt". Ésto nos ejecuta el 'make' pero en lugar de mostarnos el resultado de lo que va haciendo en la consola(pantalla), lo graba en el archivo 'make_out.txt'. En este port en particular había varias formas de compilarlo (Debug(RAM)/Target(ROM) y ARM/Thumb, es decir 4 combinaciones) preparadas en archivos batch de DOS(.bat), yo usé 'ROM_ARM.bat' y acá les pego el resultado:
(NOTA: le quité la opción -fno-dwarf2-cfi-asm al makefile porque no compila con el nuevo GNU-ARM)
Código:
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set USE_THUMB_MODE=NO 
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set DEBUG= 
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set OPTIM=-O3 
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set RUN_MODE=RUN_FROM_ROM 
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set LDSCRIPT=lpc2106-rom.ld 
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>make
arm-elf-gcc -c -Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -D RUN_FROM_ROM -D GCC_ARM7 -I. -I../../Source/include -I../Common/include  -mcpu=arm7tdmi -Tlpc2106-rom.ld -O3 -fomit-frame-pointer -fno-strict-aliasing ../../Source/portable/GCC/ARM7_LPC2000/portISR.c -o ../../Source/portable/GCC/ARM7_LPC2000/portISR.o
(...para el resto de los .c es lo mismo...)
arm-elf-gcc -Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -D RUN_FROM_ROM -D GCC_ARM7 -I. -I../../Source/include -I../Common/include  -mcpu=arm7tdmi -Tlpc2106-rom.ld -O3 -fomit-frame-pointer -fno-strict-aliasing ../../Source/portable/GCC/ARM7_LPC2000/portISR.o serial/serialISR.o main.o serial/serial.o ParTest/ParTest.o ../Common/Minimal/integer.o ../Common/Minimal/flash.o ../Common/Minimal/PollQ.o ../Common/Minimal/comtest.o ../Common/Minimal/flop.o ../Common/Minimal/semtest.o ../Common/Minimal/dynamic.o ../Common/Minimal/BlockQ.o ../../Source/tasks.o ../../Source/queue.o ../../Source/list.o ../../Source/portable/MemMang/heap_2.o ../../Source/portable/GCC/ARM7_LPC2000/port.o -nostartfiles boot.s -Xlinker -ortosdemo.elf -Xlinker -M -Xlinker -Map=rtosdemo.map
arm-elf-objcopy rtosdemo.elf -O ihex rtosdemo.hex
De donde se rescata:
  • Preprocesor Symbols:
    • RUN_FROM_ROM
    • GCC_ARM7
  • Include Paths:
    • .
    • ../Common/include
    • ../../Source/include
    • ../../Source/portable/GCC/ARM7_LPC2000
  • Misc Controls:
    • -fomit-frame-pointer
    • -fno-strict-aliasing
  • Optimization:
    • -O3
  • Linker Script File:
    • lpc2106-rom.ld
Una vez asegurados de que el GCC funciona bien solito y volviendo al tema de la configuración del uVision, no nos debemos olvidar de adjuntar el Linker Script y el Startup propio del GCC. Les muestro los parámetros mas relevantes agregados por pestaña:


En "Options for Target":





En "Setup File Extensions, Books and Environment":

Esto compila en Keil uVision sin errores y sin warnings como muestro acá:
Código:
Build target 'RTOSDemo_ARM'
assembling boot.s...
compiling ParTest.c...
compiling main.c...
compiling serial.c...
compiling serialISR.c...
compiling tasks.c...
compiling list.c...
compiling queue.c...
compiling port.c...
compiling heap_2.c...
compiling portISR.c...
compiling flash.c...
compiling comtest.c...
compiling BlockQ.c...
compiling dynamic.c...
compiling PollQ.c...
compiling semtest.c...
compiling flop.c...
compiling integer.c...
linking...
creating hex file...
"RTOSDemo.elf" - 0 Error(s), 0 Warning(s).
Claro que 'del dicho al hecho hay un gran trecho' y todo ésto si bien compila no está probado sobre en un target real, es decir, una placa de desarrollo. En teoría debería ser igual de efectivo que el port original para GCC (por razones obvias), pero se los comento para que lo tengan en cuenta.
Lo que rescato de todo ésto es que tengo un buen simulador/debugger (Keil uVision) en un entorno abierto muy portable (ARM-GCC + FreeRTOS). No quedo atado con ésto ni a compiladores propietarios (Keil RealView, IAR C/C++ Compiler, etc) ni a RTOS propietarios (Keil RTX, IAR PowerPack, Rowley CTL, etc).

 2.-Configurar el modo de funcionamiento del sistema Operativo

Una gran variedad de parametros estan disponibles para configurar este versatil sistema operativo en tiempo real. Muchos de ellos estan pensados para impactar de manera positiva en el tamaño del codigo o en la velocidad de respuesta del mismo o inclusive como va a funcionar. Todos estos parametros estan ubicados en el header file llamado "FreeRTOSConfig.h" (se incluye como ejemplo un config para PIC18F)
Código:
/*
 FreeRTOS.org V5.0.0 - Copyright (C) 2003-2008 Richard Barry.

 This file is part of the FreeRTOS.org distribution.

 FreeRTOS.org is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 FreeRTOS.org is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with FreeRTOS.org; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 A special exception to the GPL can be applied should you wish to distribute
 a combined work that includes FreeRTOS.org, without being obliged to provide
 the source code for any proprietary components.  See the licensing section 
 of http://www.FreeRTOS.org for full details of how and when the exception
 can be applied.

    ***************************************************************************
    ***************************************************************************
    *                                                                         *
    * SAVE TIME AND MONEY!  We can port FreeRTOS.org to your own hardware,    *
    * and even write all or part of your application on your behalf.          *
    * See http://www.OpenRTOS.com for details of the services we provide to   *
    * expedite your project.                                                  *
    *                                                                         *
    ***************************************************************************
    ***************************************************************************

 Please ensure to read the configuration and relevant port sections of the
 online documentation.

 http://www.FreeRTOS.org - Documentation, latest information, license and 
 contact details.

 http://www.SafeRTOS.com - A version that is certified for use in safety 
 critical systems.

 http://www.OpenRTOS.com - Commercial support, development, porting, 
 licensing and training services.
*/

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include <p18cxxx.h>

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION   1
#define configUSE_IDLE_HOOK    0
#define configUSE_TICK_HOOK    0
#define configTICK_RATE_HZ    ( ( portTickType ) 1000 )
#define configCPU_CLOCK_HZ    ( ( unsigned portLONG ) 20000000 )
#define configMAX_PRIORITIES   ( ( unsigned portBASE_TYPE ) 4 )
#define configMINIMAL_STACK_SIZE  ( 105 )
#define configTOTAL_HEAP_SIZE   ( ( size_t ) 3600 )
#define configMAX_TASK_NAME_LEN   ( 4 )
#define configUSE_TRACE_FACILITY  0
#define configUSE_16_BIT_TICKS   1
#define configIDLE_SHOULD_YIELD   1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES   0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet  0
#define INCLUDE_uxTaskPriorityGet  0
#define INCLUDE_vTaskDelete    1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend   0
#define INCLUDE_vTaskDelayUntil   1
#define INCLUDE_vTaskDelay    1


#endif /* FREERTOS_CONFIG_H */

Los parametros son:

configUSE_PREEMPTION:
Se configura con 1 que funcione como un RTOS preventivo y con 0 como un RTOS cooperativo

configUSE_TICK_HOOK
Se configura con 1 si se desea utilizar un tick externo al provisto por el proyecto 0 si se usa el propio del port.

configCPU_CLOCK_HZ
Se configura la velocidad del reloj del sistema (es un parametro que se le pasa para referencia del RTOS)

configTICK_RATE_HZ
Se configura cada cuanto se desea que se genere un tick de sistema, si es muy alto se tiene mucha definicion pero mucho overhead.
El valor de referencia es 1000

configMAX_PRIORITIES
Es el numero maximo de prioridades a utilizar, cuanto mas grande mas memoria se consume.
Se debe tener en cuenta que varias tareas pueden tener la misma prioridad (metodo de round robin para tareas de = prioridad).

configMINIMAL_STACK_SIZE
Tamaño de la pila minimo, se debe tratar de no tocar del valor de referencia de la demo.

configTOTAL_HEAP_SIZE
Tamaño de la cola de memoria disponible para el RTOS. De aqui se alimenta el sistema operativo para su manejo (colas, semaforos, mutex, etc).

configMAX_TASK_NAME_LEN
Esta es la longitud maxima de nombre de la tarea

configUSE_TRACE_FACILITY
Utilizado para trazabilidad.

configUSE_16_BIT_TICKS
Tamaño de los registros de conteo de Ticks, impacta directamente en el tiempo total posible a contar, en la memoria utilizada para tal fin y el overhead generado por conteos con mas longitud de palabra.
Depende mucho de la arquitectura del micro (8, 16 o 32 bits)

configIDLE_SHOULD_YIELD
Util solo si el RTOS esta configurado en modo preventivo y se usa la tarea IDLE.
Afecta al funcionamiento de las tareas en IDLE, su eficiencia y overhead.

configUSE_USE_MUTEXES
Si se setea en 1 se utilizan MUTEXES

configUSE_RECURSIVE_MUTEXES
SI se setea en 1 se pueden usar MUTEXES recursivos

configUSE_COUNTING_SEMAPHORES
Si se setea en 1 los semaforos no seran binarios sino que contaran numero de ocurrencias a costo de mas memoria.

configUSE_ALTERNATIVE_API
Si se setea en 1 se utilizan APIs alternativas que tienen menor costo en peso de codigo y velocidad a costa de usarlas con consideraciones.

configCHECK_FOR_STACK_OVERFLOW
Si se setea en 1 se checkea por overflow del stack.

configUSE_CO_ROUTINES
Si se setea en 1 se utilizaran corutinas (debe incluirse la source corutine.c) para manejo cooperativo (puede ser mixto con preventivo)

configMAX_CO_ROUTINE_PRIORITIES
Numero de prioridades de las corutinas

configKERNEL_INTERRUPT_PRIORITY
Solo disponible para PIC24/DSPIC y Cortex M3 processors, se puede configurar las prioridades de las interrupciones y como afectan al RTOS.

PARAMETROS INCLUDE
Esto es una funcionalidad muy buena del RTOS, permite anular del proceso de BUILD (compilacion), a toda funcion que no se desea con solo poner la definicion con su nombre de la siguiente forma.

#define INCLUDE_vTaskDelete    1    Aqui se incluye en el proceso de build a la tarea "borrar Tarea"
#define INCLUDE_vTaskDelete    0    Aqui se excluye del proceso de build a la tarea "borrar Tarea"

3.-Tipos de variables y convencion de nombres

Aunque se pueden utilizar los tipos de variables que provee tu compilador, y a manera de unificar entre diferentes plataformas, el FREERTOS utiliza tipos de variables propios que no son mas que definiciones de tipo que son actualizados en tiempo de compilacion. De esta manera, el codigo que se realiza para un compilador "X" funciona en otro compilador "Y".

Los tipos estandar son:
portCHAR      Es un char, una palabra entera de 8 bits o un caracter ASCII estandar
portFLOAT       Es un numero con punto flotante de precision simple
portDOUBLE      Es un numero con punto flotante de precision doble
portLONG      Es un entero de 32 bits
portSHORT       Es un entero de 16 bits
portSTACK_TYPE es el tipo de datos y tamaño consumidos por el stack de memoria

Hay dos tipos de variables muy fuertemente unidos a la arquitectura del micro:
portBASE_TYPE   Es el tipo de variable entera basica atada al tipo de procesador, el que genera menos overhead.
portTickType       Es configurable en longitud (16 o 32 bits) dependiendo de lo elegido en la Configuracion del RTOS

Convencion de nombres
Las aplicaciones que desarrolles bajo el RTOS deberian cumplir con las reglas de nombres que se establecen para una mejor lectura y comprension del codigo.
Los nombres de las funciones utilizan el metodo lower camelcase donde el primer caracter hace referencia al tipo de variable que se retorna.

Si se retorna void lleva al principio v
Si se retorna char lleva al principio c
Si se retorna short lleva al principio s
Si se retorna long lleva al principio l
Si se retorna double lleva al principio d
Si se retorna variables enumeradas lleva al principio e
Si se retorna otros tipos (por ej estructuras) lleva al principio x
Si se retorna un puntero a un tipo de datos se pone adicionalmente p (ej: ps es puntero a un short)

Adicionalmente, si la variable es sin signo (unsigned) se pone delante una u.
Si son funciones privadas, adicionalmente se pone prv
Siempre se hace referencia en el nombre al modulo en donde se han definido (por ejemplo vTaskDelete esta en el modulo task.c).

4.-Inicializacion del RTOS, creacion y destruccion de tareas

Una vez que tenemos el ambiente de desarrollo listo, solo nos queda agregar en el programa principal (main) las llamadas a las APIs que se encargan de:
  • Inicializar la memoria del sistema
  • Ejecutar el Scheduler o administrador de tareas
  • Ejecutar las tareas padre.

Para la inicializacion de la memoria se llama a la funcion vPortInitialiseBlocks() que depende directamente del port y del modelo de memoria utilizado. Para ejecutar el Sheduler y comenzar el proceso de administracion se llama a la funcion vTaskStartScheduler, esta funcion tambien se encarga de la configuracion previa del periferico encargado para el tick del sistema y de las interrupciones.
Si tuvo exito en su arranque no retorna nunca mas a menos que se llame a la funcion vTaskEndScheduler que permite continuar la ejecucion justo en el punto en donde se ejecuto vTaskStartScheduler.

1.Para ejecutar una tarea especifica se llama a la funcion xTaskCreate que toma los siguientes parametros en el mismo orden:

    pvTaskCode    Puntero al nombre de la tarea.
    pcName    Un nombre auto descriptivo de la tarea para ayudar en el proceso de debug. la longitud max se define con configMAX_TASK_NAME_LEN.
    usStackDepth    Es el tamaño del stack de tareas definido como el nro de variables que el stack puede almacenar, no el nro de bytes.
    pvParameters    puntero a los parametros que va a recibir la tarea.
    uxPriority    La prioridad con la que va a correr la tarea (depende del nro de prioridades que se configuro)
    pvCreatedTask    Se usa para pasar el manejador por la cual la tarea puede ser referenciada.

Devuelve un indicador de exito (pdPASS) o fracaso (un error code) en la creacion de la tarea.

Ejemplo:
Código:
void main( void )
{
xTaskHandle xHandle;

 /* Inicializo Hardware. */
 vPortInitialiseBlocks();

 /* Arranco la tarea */
 xTaskCreate( vTareaDePrueba, ( const portCHAR * const ) "Prueba", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, Handle );

 /* Arranco el scheduler. */
 vTaskStartScheduler();
}

2. Para destruir una tarea y recuperar la memoria que utilizaba en la cola, se debe llamar a la funcion vTaskDelete donde el unico parametro que se le pasa es el el manejador a la tarea.

Código:
void vDestruirMiTarea( void )
{
    vTaskDelete( xHandle );
}

Tareas Suicidas:
Agregue un pequeño ejemplo en donde se arranca una tarea que crea tiene como funcion crear otra tarea y se autodestruye.
La segunda realiza la misma tarea que la primera aunque no comparte codigo en comun.

5.-Consideraciones para Multiples instancias de la misma tarea 


De la misma manera que en un Sistema Operativo clasico, ya podemos generar multiples instancias del codigo de una sola funcion. Cada una de ellas tendra, gracias al RTOS, una porcion de los recursos del micro, para alojar sus datos y ejecutarse correctamente. Los recursos que pidamos a traves del RTOS seran instanciados con su tarea especifica, pero que pasa con las variables declaradas en la funcion?
Son instanciadas en conjunto con la tarea!!, cada Tarea poseera sus propias posiciones de memoria para sus variables. Esto lo podemos constatar mirando la ubicacion de dichas variables en el mapa de memoria. En el siguiente ejemplo se crean 2 instancias de la misma funcion, donde cada una recibe un parametro diferente que las identifica (mas alla del nombre que se le pasa).

Código:
void main( void )
{
    /* Inicializo Hardware. */
 vPortInitialiseBlocks();

 /* Arranco la tarea */
 xTaskCreate( vTarea, ( const portCHAR * const ) "Prueba1", configMINIMAL_STACK_SIZE, &cProcId1, tskIDLE_PRIORITY + 2, NULL );
 xTaskCreate( vTarea, ( const portCHAR * const ) "Prueba2", configMINIMAL_STACK_SIZE, &cProcId2, tskIDLE_PRIORITY + 2, NULL );
    
 /* Arranco el scheduler. */
 vTaskStartScheduler();
}

Luego cada instancia chequea ese parametro para saber cual de las dos instancias se esta ejecutando en dicho momento.

Código:
static void vTarea( void *pvParameters )
{
portCHAR *cProcId,cVariableUnica;

    for( ;; )
 {
  /* Demoro 100 Ticks */
     vTaskDelay( 100 );
        cProcId = pvParameters;`
        if (*cProcId == 1)
        {
            cVariableUnica = 1;
            portNOP();
        }
        else if (*cProcId == 2)
        {
            cVariableUnica = 2;
            portNOP();   
        }
        else
        {
            portNOP(); //Caso de error no deberia entrar aca
            cVariableUnica = 3;
        }
 }
}

Si visualizamos el mapa de memoria veremos que cuando se ejecuta cada una de las tareas se muestra para la misma variable una direccion diferente de memoria.



6.-Uso de las prioridades

Ahora que ya podemos crear tareas debemos pensar en como utilizar eficiente y responsablemente el tiempo del micro. Eficientemente porque obviamente hay tareas que no requieren mucho tiempo de procesador ni tanta prioridad para completarla y responsablemente ya que configurando prioridades mal en conjunto con una mala programacion pueden producir estragos en el funcionamiento del sistema.

A continuacion pondre una serie de ejemplos:
Tenemos 3 tareas:
  • 1.Atender pedidos por usart.
  • 2.Atender teclado
  • 3.Realizar una tarea de procesamiento ininterrumpida muy larga.

EJ 1:
Evidentemente en este sistema tendremos prioridades, y para el caso usaremos 3.
1.Los pedidos de usart deberian tener la MAXIMA prioridad, ya que deben atenderse inmediatamente despues de recibirse.
En el instante posterior de haber concluido dicha tarea, se vuelve a dormir y otras tareas de menor prioridad pueden ejecutarse.
2.Atender el teclado tambien es un evento asincronico, que interrumpe en un instante y luego se vuelve a dormir. Es menos prioritario que el anterior.
3.Una tarea de mucho tiempo de procesamiento tendra menor prioridad ya que pedira cuanto tiempo de micro se tenga, por ende se hace cuando no hay nada mas que hacer.

EJ2: Que pasa si la tarea 3 tuviese la maxima prioridad?
Como es una tarea que nunca se duerme (por ej para relegar procesador o para esperar un evento), estara siempre activa deseosa por tiempo de procesador. Si es la tarea de mayor prioridad, las tareas de menor prioridad NUNCA se ejecutaran, porque compiten contra una tarea mas prioritaria, por ende dichas tareas MUEREN DE INANICION.

EJ3: Que pasa si las tareas 1 y 2 tuviesen la misma prioridad? (y la 3 fuese de menor prioridad)
En este caso, y si es que ambas tuviesen que ejecutarse, el RTOS se encargaria de compartir entre las dos el tiempo de micro por el mecanismo de Round Robin.
Este mecanismo simplemente cicla entre las tareas de igual prioridad.

EJ4: Y si las tareas 1 y 2 son las mas prioritarias y nunca relegan el micro para la tarea 3?
Entonces tendria que existir un mecanismo de incremento de la prioridad de la tarea 3 para evitar su muerte por inanicion.
La encargada de dicha funcion seria una tarea de la mas alta prioridad que chequea cada un periodo de tiempo que todas las tareas tengan oportunidad de ejecutar y para las que no lo logran se les aumenta la prioridad.
Una vez que se logra inmediatamente se le retorna su prioridad nominal.

7.-Control sobre la tarea

El sistema operativo FREERTOS da una serie de APIs orientadas al control optimo de la tarea en curso, osea que generen un cambio en las condiciones del mismo sin necesidad de otras fuentes externas (semaforos, colas, mutex, etc).
Ellas son:
  • vTaskDelay:
    Genera una demora en la ejecucion de la proxima tarea medida en ticks de sistema(dicha demora NO es a base de consumo de ciclos de reloj) .
  • vTaskDelayUntil:
    Genera una demora hasta un instante de tiempo especifico, ideal para poder realizar periodos ciclicos con una frecuencia fija.
    Se basa en el uso del reloj del sistema.
  • uxTaskPriorityGet:
    Recupera la prioridad de una tarea entregandosele la informacion de su handler.
  • vTaskPrioritySet:
    Altera la prioridad de una tarea entregandosele la informacion de su handler.
  • vTaskSuspend:
    Suspende la ejecucion de una tarea, no importa la prioridad que ella tenga.
  • vTaskResume:
    Continua la ejecucion de una tarea suspendida.
  • vTaskResumeFromISR:
    Idem anterior pero adaptada para poder ser llamada desde interrupciones.

8.-Control sobre el Kernel

Control sobre el Kernel:
De la misma manera que con las tareas, el RTOS provee herramientas para el control del funcionamiento del sistema operativo.
En su mayoria son funciones que proveen soporte y proteccion a dilemas de programacion como son las secciones criticas  con recursos compartidos entre las tareas y el kernel (secciones criticas a nivel kernel) y las secciones criticas a nivel de aplicacion, para ello estan las siguientes APIs y los metodos de sincronizacion entre tareas.

  • vTaskStartScheduler:Comienza el funcionamiento del sistema operativo.
  • vTaskEndScheduler:Termina el funcionamiento del sistema operativo, el codigo siguiente a ejecutar es el que se encuentra despues de la llamada a vTaskStartScheduler.
    Es muy util cuando se desea hacer uso del procesador para una tarea dedicada como actualizacion del firmware, etc. Luego puede llamarse nuevamente a vTaskStartScheduler para continuar con el funcionamiento del RTOS.
  • taskDISABLE_INTERRUPTS: Deshabilita TODA fuente de interrupcion y con ello el analisis de cualquier fuente de la misma.
    Se utiliza para los casos mas estrictos de SECCION CRITICA donde NADA puede interrumpir a las intrucciones posteriores.
    Se usa en conjunto con taskENABLE_INTERRUPTS para delimitar la zona critica.
    Su uso mas habitual es cuando el procesador debe estar totalmente devoto a una tarea critica en tiempos mas que por uso de recursos compartidos (aunque puede tambien serlo).
  • taskENABLE_INTERRUPTS:Rehabilita TODAS las fuentes de interrupcion.
  • taskENTER_CRITICAL: Deshabilita la fuente de interrupcion ligada al Tick del sistema y acceso al algoritmo preventivo.
    Este es otro tipo de seccion critica de kernel donde se protege los recursos (puede ser solamente datos) compartidos entre las tareas o Kernel y las interrupciones al tick de sistema y al algoritmo preventivo.
    Otras fuentes de interrupcion siguen siendo funcionales (USART, SPI, INTs, etc)
  • taskEXIT_CRITICAL: Rehabilita las fuentes de interrupcion provenientes del tick del RTOS.
  • vTaskSuspendAll: Se da comienzo a una seccion critica de sistema operativo a nivel de aplicacion.
    Se atienden todos los tipos de interrupciones (se cuentan los ticks por ejemplo) pero no se generan cambios de contexto de tareas.
    Esto es para evitar secciones criticas entre dos tareas que acceden a un mismo recurso y por ser de corto plazo no es necesario utilizar mecanismos de sincronizacion y exclusion mutua.
  • xTaskResumeAll: Se rehabilita el modo preventivo de las tareas.
  • taskYIELD: Fuerza al sistema operativo a realizar un cambio de contexto.
    Esta pensado especificamente para el modo de trabajo cooperativo pero funciona bajo modo preventivo igualmente.
    Cuanto mas se la llame en una tarea mas granularidad se tendra pero se generara mas overhead por uso del sistema operativo.

9.-Semaforos

Los semaforos son la herramienta que posee el Sistema Operativo en tiempo real para realizar coordinacion entre tareas.
Dos de los usos habituales de los semaforos son:
  • 1.Sincronizacion para la toma de un recurso comun: Este no es mas que otro modo de resolver secciones criticas como se explica en el post de control sobre el kernel
    Lo que se busca es utilizar un semaforo como "llave" para el uso de un recurso compartido. Si dicha llave esta en uso no se puede acceder a dicho recurso, o se espera por ella o se indica el error pero no se utiliza el recurso de manera que se protege el mismo de un uso incorrecto.
  • 2.Sincronizacion para la atencion de eventos asincronicos: En este caso se utiliza al semaforo como indicador de evento y disparo de actividades.
    Es un caso simple del uso de colas, donde en este caso no se posee mensaje a enviar.
    Una tarea es la generadora de eventos (en FREERTOS la que hace Give) y la otra tarea que espera dicho evento (en FREERTOS la que hace Take).

Se puede computar  cual sera el intervalo de tiempo que se espera por el semaforo, si no se tiene exito en tomarlo se retorna luego de dicho tiempo y se indica con error. Si se logro capturar se despierta a la tarea que llamo a xSemaphoreGive() y se indica exito.

Semaforos binarios


Mutexes


Las funciones para semaforos son:
  • 1.De creacion
    vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
    Crea un semaforo BINARIO si es que xSemaphore no retorna NULL.

    xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount )
    Igual al anterior, crea un semaforo pero con capacidad de conteo, delimitado por uxMaxCount que es la cantidad maxima posible a contar y uxInitialCount que es el valor inicial de conteo.
    Tambien se puede usar como 1.contador de eventos  o 2.Administracion de recursos.

    xSemaphoreHandle xSemaphoreCreateMutex( void )
    Crea un semaforo de caracteristicas binarias pero con capacidad de exclusion mutua, que significa que posee manejo de prioridades.
    En un caso de competencia, gana el mutex la tarea que tenga mayor prioridad.

    xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void )
    Crea un Mutex recursivo.
  • 2.De toma de recurso o espera de evento
    xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xBlockTime )
    Cuando se llama a esta funcion, la tarea esperara "xBlockTime" de tiempo por el semaforo "xSemaphore", luego de ese tiempo si no se libero (o genero evento) se reanuda la ejecucion de la tarea y se informa error.
    Si se libero se captura el semaforo para asegurar que nadie utilizara dicho semaforo.

    xSemaphoreTakeRecursive( xSemaphoreHandle xMutex, portTickType xBlockTime )
    Toma de manera recursiva un semaforo para asegurarselo tantas veces como se llamo a la funcion.
  • 3.De entrega de recurso o generacion de evento
    xSemaphoreGive( xSemaphoreHandle xSemaphore )
    En este caso se libera al semaforo para que otras tareas puedan usar el recurso asociado con la misma o se genera un evento para despertar a la tarea que espera por dicho semaforo.

    xSemaphoreGiveRecursive( xSemaphoreHandle xMutex )
    Se devuelve dicho semaforo de manera recursiva.
    Para que otra tarea lo pueda usar, la tarea que tiene capturado dicho semaforo debe llamar a esta funcion tantas veces como antes llamo a xSemaphoreTakeRecursive.

    xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken )
    Igual que xSemaphoreGive, pero con contemplaciones especiales para su uso desde interrupciones.

En "ejemplo_semaforos_evento.ZIP" se utiliza un semaforo binario como generador de evento, recuerden posicionarlo dentro de la carpeta "Projects" para que compile y corrijan los path a la carpeta de su compilador C18.

En "ejemplo_semaforos_recurso.ZIP" se utiliza semaforo binario como "llave" al recurso "puerto C" del microcontrolador.
Una tarea quiere escribir hacia el puerto y la otra quiere leer de su entrada, para ello uno requiere configurarlo en salida y escribir y el otro configurarlo en entrada y leer.
Con el uso de un semaforo arbitramos su uso de manera correcta sin que se mezclen pasos de ambas tareas por culpa del context switch del RTOS.

10.-Colas

Trabajo en proceso
El uso de colas (queue) es el metodo estandar de comunicacion entre procesos o tareas.
Una cola es una estructura de datos, caracterizada por ser una secuencia de elementos en la que la operación de inserción "push" se realiza por un extremo y la operación de extracción "pop" por el otro. También se le llama estructura FIFO (del inglés First In First Out), debido a que el primer elemento en entrar será también el primero en salir (segun Wikipedia).

En sistemas operativos, cuando una tarea desea enviar un mensaje a otra, "empuja" el mensaje a dicha cola (si es que hay lugar en la misma, sino debera esperar o informar error) por un extremo.
Del otro lado habra una tarea a la espera de mensajes y cuando uno llega se despertara para analizarlos realizando una "extraccion" por el otro extremo de la cola.



En FREERTOS el metodo de creacion de una cola y asignacion de un espacio de memoria esta dado por la funcion:
 xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize );

Donde xQueueHandle almacena la referencia a la instancia, si retorna NULO o cero es que no pudo crearlo.
uxQueueLength la cantidad de posiciones que tendra la cola y uxItemSize representa cantidad de datos que consumira en bytes cada elemento.

Para enviar mensajes se dispone de portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );

Para recibir mensajes se dispone de portBASE_TYPE xQueueReceive( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait );

Donde xTicksToWait representa el tiempo que se esperara para posicionar el mensaje en la cola o esperar por un mensaje de la misma.
Si se setea INCLUDE_vTaskSuspend en 1 con el valor portMAX_DELAY se suspende por tiempo indefinido la tarea hasta que se libere espacio en la cola o se reciba un mensaje dependiendo el caso.
xQueue representa la cola por donde se mueven los mensajes y pvItemToQueue el mensaje en si.

Ambas funciones retornan como resultado el exito (pdPASS) o fracaso (un codigo de error) en la tarea.

El ejemplo a continuacion representa a dos tareas que se envian un mensaje del tipo char que se va incrementado de a uno por exito en cada tarea.
Para demostrar el correcto uso de colas, si alguna de las dos no envia el mensaje en contestacion o no lo recibe por error las tareas se duermen.
Como siempre ubiquenlo en la carpeta de proyectos que creamos.

miércoles, 9 de abril de 2014

RTOS pars Raspberry Pi?

ChibiOS / RT es un sistema portátil en tiempo real de funcionamiento ( RTOS ) diseñado para aplicaciones embebidas. Aunque el Raspberry Pi se suele utilizar con el sistema operativo Linux, que no es necesariamente la mejor opción para aplicaciones que requieren baja latencia , la respuesta previsible a los acontecimientos externos.  


Incluso si usted no tiene una aplicación con estos requisitos ChibiOS / RT puede ser útil para aprender sobre los sistemas operativos integrados y temas relacionados, como la compilación cruzada , hilo de cambio de contexto, control de concurrencia , el tratamiento de alarmas y el desarrollo de controladores de dispositivo. El código base es mucho más pequeña que la de Linux y por lo tanto mucho más accesible y fácil de entender .  


 Otra ventaja es que el sistema operativo y de las aplicaciones se pueden ejecutar en otros equipos, como el Arduino, que no son compatibles con Linux.

He aquí una breve lista de características ChibiOS / RT ( desde el sitio web ) :

  •     Kernel preventivo eficiente y portátil.
  •     El mejor rendimiento cambio de contexto de clase.
  •     Muchas arquitecturas y plataformas soportadas .
  •     Arquitectura estática , todo se asigna estáticamente en tiempo de compilación .
  •     Extensiones dinámicas , los objetos dinámicos son apoyados por una capa opcional incorporado en la parte superior del núcleo estático.
  •     Rico conjunto de primitivas : hilos , temporizadores virtuales , semáforos , exclusiones mutuas , variables de condición , mensajes, buzones de correo, banderas de eventos, colas.
  •     Soporte para el algoritmo herencia de prioridad en las exclusiones mutuas .
  •     Hardware Abstraction Layer (HAL ) Componente de apoyo a una variedad de controladores de dispositivos abstractos : Port , Serial , ADC , CAN , EXT , GPT, I2C, UCI , MAC , MMC , PWM , RTC, COSUDE , SPI , UART , USB , USB- CDC .
  •     El apoyo a los componentes externos UIP , lwIP , FATFS .
  •     Banco de pruebas extensas con los puntos de referencia .
  •     Soporte para aplicaciones C + + .

martes, 8 de abril de 2014

Sistemas Operativos Vs Programación PFI. Un tutorial para iniciarte en el mundo de OS para micros.

A continuación presento un resumen de un material o tutorial de un curso de RTOS para FreeRtos que considero excelente y mejora sustancialmente a muchos tutoriales en la red si con algunas observaciones en cuanto a gráficos. (Ing. Reinier Torres Labrada)

FreeRTOS tiene características comunes con el resto de sus parientes, y algunas propias, que poco a poco iremos viendo y analizando cada vez con mayor nivel de detalle, hasta lograr dominar cadauna de sus poderosas características.

Características Generales:
Núcleo de tiempo real minimalista, de código abierto,de libre distribución y que puede ser utilizado en aplicaciones comerciales sin que el desarrollador requiera una licencia por su utilización.
FreeRTOS está protegido por la licencia GPL, e incluye una excepción para permitir su uso en  aplicaciones comerciales sin la obligatoriedad de que el código escrito para aplicación tenga que ser liberado bajo GPL
Amplia disponibilidad de puertos para diferentes arquitecturas de procesadores y herramientas de desarrollo. Con cada distribución se incluye un conjunto de aplicaciones demostrativas para ayudar al principiante a introducirse en el uso del núcleo. Soporte gratuito a través de una amplia y activa comunidad de usuarios. Adicionalmente existe soporte comercial que incluye asistencia en la asimilación y desarrollo de aplicaciones.

Características Técnicas:
Núcleo configurabe en tres modalidades:
De tiempo compartido:
El núcleo siempre otorgará el procesador a la tarea de mayor prioridad que esté lista para ser ejecutada. Si varias tareas están listas para ejecutarse y comparten el mismo nivel de prioridad; se aplica la técnica Round-Robin para determinar cuál de las disponibles obtendrá el procesador por el próximo período de ejecución. Es el modo
de trabajo por defecto.

De tiempo cooperativo:
Es responsabilidad de las tareas entregar el procesador para que el planificador la entregue a la tarea de mayor prioridad que esté lista para ser ejecutada. Las interrupciones no pueden provocar un cambio de contexto.
 

Híbrido:
Las interrupciones pueden provocar el cambio de contexto hacia una tarea, pero el cambio de contexto entre tareas se realiza mediante la técnica de tiempo cooperativo

  • Diseño minimalista que necesita muy pocos recursos para su implementación. Dependiendo de la arquitectura del procesador se puede acomodar una implementación mínima en el espacio de memoria requerido para 2000 instrucciones.
  • Diseño modular, personalizable y fácil de utilizar.
  • La mayor parte del código está escrito en C estándar. Habitualmente existe una parte implementada en ensamblador, dedicada a las funcionalidades específicas de cada arquitectura del procesador.
  • Funcionalidades para el seguimiento de tareas y estadísticas de desempeño.
  • Soporte de tareas y co-rutinas.
  • Detección de desbordamiento de pila.
  • Sin restricciones en el número de tareas.
  • Sin restricciones en el número de prioridades.
  • Sin restricciones en la asignación de prioridades, tareas distintas pueden compartir el mismo nivel de prioridad con planificación Round-Robin.
  • API del núcleo que incluye el manejo de: Colas, Semáforos binarios de conteo y recursivos de Exclusión mutua.
  • Herramientas de desarrollo amparadas por software libre.
  • Código fuente amparado por licencia de software libre.
  • Libre de cargos comerciales, aún cuando se utilice en aplicaciones comerciales.
  • Aplicaciones de ejemplo disponibles para todas las arquitecturas soportadas y respaldadas en tarjetas comerciales, principalmente de bajo costo.
¿Por qué es una buena opción?
  • Es una única solución que cubre diferentes arquitecturas.
  • Es reconocido por su buen desempeño.
  • Soportado por una comunidad muy activa y creciente.
  • Demanda pocos recursos de memoria RAM y ROM.
  • El kernel está contenido en sólo tres ficheros de C.
  • Usted tiene acceso al código fuente y pude modificarlo y adaptarlo a sus necesidades si así lo requiere.
¿Y cómo trabaja?

La misión de un sistema operativo es proveer a desarrolladores y usuarios de funcionalidades para el uso eficiente de los recursos del sistema de cómputo.


Programar bajo este nuevo paradigma incluye un mecanismo estructural de diseño escalable, ventaja para sistema de desarrollo con integración futura.

Es conocido que el recurso más preciado de un sistema con microprocesador es el procesador, los microcontroladores no escapan a esta realidad. Para complicar más las cosas, el procesador consume tiempo para ejecutar código así que en la mayoría de los casos hacer las cosas a tiempo constituye el principal cuello de botella durante el diseño de sistemas empotrados.

Además de ayudarnos a gestionar eficientemente el procesador; FreeRTOS es una poderosa herramienta para enfrentar el problema de la complejidad de las aplicaciones, el acceso a los periféricos del microcontrolador, liberar y reservar memoria en función de la carga del sistema. Para ello disponede tres elementos esenciales comunes a cualquier sistema operativo.
  • Planificador de tareas (Scheduller)
  • Interfaz de Aplicación para el Programador (API)
  • Manipuladores de dispositivos (Device Drivers)
Estas tres funcionalidades se implementan mediante funciones de librería de C estándar y código específico para cada arquitectura de procesador, de este modo se garantiza la reutilización del código del núcleo y el aprovechamiento de cualquier arquitectura que cuente con los recursos necesarios para soportar al sistema operativo.

Scheduller
Es el encargado de asignar el procesador a quién en ese momento le corresponda utilizarlo, para ello FreeRTOS tiene implementada una de las más conocidas políticas de asignación de recursos que se concen en el mundo de los sistemas operativos. Más adelante entraremos en detalles acerca de cómo funciona el scheduller de FreeRTOS.

API
FreeRTOS no puede darse el lujo de implementar muchas funcionalidades porque su misión fundamental es ser ligero y rápido, así que lo que más encontraremos acá serán las funcionalidades para sincronización de tareas y alguna que otra función para el trazado y recopilación de estadísticas.
Los sistemas operativos de propósito general acostumbran tener una API grande y compleja, pero en un microcontrolador los recursos son limitados en todo sentido, así que para suerte nuestra tendremos pocas funciones que dominar, y para desgracia, tendremos que aprender a sacarles el máximo.

OS vs Programacion de funciones o programación standar.
Si piensa en el programa mas complejo que allas realizado muy probablemente el programa consta de lo siguiente:
Programa principal. conformado por un lazo infinito en donde conviven funciones o procesos que se activan mediante un swiche lógico. Dicho swiche lógico o If se encuentra consultado el resultados de un macro contador para selección. Como lo anterior, esas funciones o subrutinas poseen llamados de interrupciones compactas de configuración, acción y salida (como para ser claro y juste en temas de prioridades)

Si nunca ha trabajado con un Sistema Operativo, es muy probable que todos los programas desarrollados por usted trabajen de la misma filosofía, en lo que podríamos llamar modelo de programación principal, las funciones y las Interrupciones; yo les llamo "PFI".Por lo anterior todos los programas bajo este concepto tienen un elementos común que les permite trabajar armónicamente y nada desordenado la memoria de datos y llevar un control de las funciones activar o desactivar.


La Figura siguiente se muestra claramente este modelo de desarollo de aplicaciones estandard y ampliamente utilizado en el diseño con Microcontroladores y también en otros ámbitos. A muchos podría parecerle magnífico, y en muchos casos es así, simplemente porque es sencillo, Pseudo-escalable. Veamos, sí aplica una codición habilita una función que modifica la memoria interna o externa del microprocesador lo mismo con las interrupciones.
Si pensamos en realizar un reloj con display de leds. Se propone el uso de un Timer1 en formato Interrupción para barrido de leds con salida correspondiente de lectura de memoria y puerto; esto con el fin
de hacer independiente la salida visual vs el programa principal en donde se calcula problemas de tiempos e incrementos con funciones (de baja prioridad). Una vez finalizada la condición se devuelve al programa principal y en espera de algún cambio de los macrocontadores que activen dichas condiciones.
En este caso el programa principal decide, mediante encuesta que función se ejecutará, pero una vez que la función está ejecutándose el programa debe esperar pacientemente a que termine, antes de seguir encuestando las condiciones para evaluar si otras funciones deben ser llamadas o no. El único caso en que una función llamada o el programa principal pueden ser interrumpidos es cuando se produce una interrupción.

Este modelo, sin embargo, tiene una desventaja intrínseca que se manifiesta más en la medida que aumenta la cantidad de “cosas” que el sistema debe hacer. Si además, esas “cosas” tienen que hacerse respetando restricciones de tiempo en la ejecución misma del código de la “cosa” o en el tiempo en que la “cosa” debe ejecutarse, estaremos ante un problema al estilo Nudo Gordiano.

Ahora imagine que cada “cosa” de este programa que estamos analizando fuese la única por hacer, evidentemente el programa sería muy simple. Ese es precisamente el concepto tras un sistema operativo, hacerle creer al sistema que tiene una única tarea que hacer en cada momento, pero hacerlo de forma tal que no se note que estamos conmutando entre las distintas “cosas”. Por lo tanto llamaremos tarea
a eso indefinido que he llamado “cosa”.

Este cambio de concepto nos permite implementar las funcionalidades como si fuesen la única actividad a la que estará dedicado el procesador del microcontrolador, es algo así como que cada tarea es un programa principal de allí sale el concepto de programación pseudo paralelo. Sin embargo, sabemos que eso no puede ser, puesto que una aplicación cualquiera debe hacer varias tareas diferentes, incluso relacionadas entre ellas y cada una debe pasar por un tiempo en el microcontrolador. Entonces  ¿si cada tarea es como un programa principal? ¿cómo se ejecutarán todas? ¿y cuando? ¿y si están relacionadas entre sí?.

Las respuestas a estas y otras preguntas irán llegando poco a poco en la medida que descubrimos un nuevo modelo de programación.


Ahora veamos la figura superior. Note como ha desaparecido el programa principal, sin embargo sí que se mantienen las Interrupciones, pero hay dos nuevos elementos, las tareas y el núcleo del sistema operativo. Las interrupciones forma parte de un proceso casi a la par de las Tareas.
Observe que el sistema operativo es el encargado de decidir que tarea se ejecuta. Note además, que las tareas intercambian información entre ellas a través de la memoria y del núcleo del sistema operativo. Asimismo, observe que existen algunas tares desactuvadas mientras hay tareas en ejecución. Y todo absolutamente gobernado por el Sistema Operativo.


En este caso pueden estar en ejecución una tarea, el programa principal o un ISR. Sin embargo, usted no programa nada en el núcleo del sistema operativo, sino que se concentra en hacer bien el código de las tareas. El núcleo se encargará de decidir que tarea obtendrá el procesador y además de eso, como veremos más adelante, el núcleo puede “interrumpir” a una tarea que se esté ejecutando. Como en el caso anterior las interrupciones pueden entrar en contexto cuando el núcleo o una tarea están ejecutando código.


Una vez mostrado las diferencias en programación sobre estas dos filosofía de programación lo invito adentarnos un poco mas sobre los conceptos de OS y su empleo en los sistemas embebidos.

Conceptos de Sistemas Operativos

Tareas
Entidad que encapsula en sí misma código y memoria de datos y que actúa como si fuese el única programa en ejecución en un sistema de cómputo. Note el esquema de una Tarea en la figura derecha, existe en el espacio de datos un elemento muy especial el TCB.
El TCB (Bloque de Control de Tarea) es el espacio de memoria donde el núcleo del sistema Operativo y la tarea del mismo almacenan parte de la información que permitirá, entre otras cosas, decidir si la tarea puede o no ejecutarse. Recordemos que las tareas actúan como si ellas fuesen el único programa en el sistema. Esto es cierto si la tarea está en ejecución, pero si no es así, es como si la tarea estuviese “suspendida” o “congelada”. De modo que la mayor parte del tiempo nuestro sistema de cómputo debe tener una tarea en ejecución y seguramente otras tantas “suspendidas” o “congeladas” y es por ello que sus espacios de memoria de datos deben estar protegidos de posibles ataques externos, por eso un bloque protector verde en la memoria. Poniendo todo junto y suponiendo que tenemos un sistema con un procesador; podríamos ver un escenario como el de la Figura siguiente. Observe como en un sistema con un único procesador solamente una tarea puede estar ajecutando su código y accediendo a su memoria, el resto está en estado de “no ejecución” o “ejecución suspendida”.

Ejecución Suspendida

Se define ejecución suspendida como un “super-estado” no nos sirve de mucho puesto que una tarea puede estar aquí por diferentes razones, veamos algunas:
  • Porque aún estando lista para ejecutar código, una tarea más importante está ejecutando su código.
  • Porque la tarea misma necesita esperar por un evento.
  • Porque la tarea terminó de ejecutar su código y no necesita volver a ejecutarlo hasta pasado un tiempo.
  • Porque la tarea ha intentado acceder a un recurso compartido y este está ocupado.
Pero en resumen se puede listar las dos razones mas importante:
  • La tarea está lista para ejecutar código pero no puede.
  • La tarea espera por algo.
El primer caso se conoce como “tarea lista” mientras que el segundo es conocido como “tarea bloqueda” y si añadimos el estado “en ejecución” tenemos los tres estados básicos en que puede estar una tarea.

Observe que una tarea puede pasar directamente del estado “en ejecución” a los estados “lista” o “bloqueada” y que los cambios de “bloqueada” a “en ejecución” o de “lista” a “bloqueada” estan prohibidos. Respetando estas reglas podemos obtener un modelo de trabajo simple que nos podría permitir gestionar eficazmente el acceso de las tareas al procesador.

El caso de que una tarea se bloquea a sí misma porque deba esperar por un evento de alguna clase, es muy fácil de comprender. Sin embargo, no queda claro cómo es que una tarea puede pasar del estado “en ejecución” a “lista” ya que si está lista para ejecutar código, no es lógico que la tarea entregue el procesador???
Las tareas son egoístas en muchas ocasiones es un simil. Si tienen el procesador y pueden estar ejecutando código, ellas no lo entregarán, es por ello que se requiere la “intervención divina” o "Interrupción divina" para quitarle el procesador al egoismo de la taréa y darlo a otra que lo necesita y que también está lista para tiempo de proceso.

Planificador de tareas

Ya hemos visto que si una tarea está en condiciones de ejecutar código no querrá ceder el procesador, por lo que se requiere la intervención de otro agente en este juego que le retire el procesador y se lo pase a otra.
Ese agente es el planificador de tareas (scheduller) y su trabajo consiste en repartir el tiempo de procesamiento del sistema de forma que todos tengan el procesador cuando lo necesiten para hacer bien su trabajo y que nadie se adueñe de él indiscriminadamente.

Pero si el scheduller es un programa ¿cómo se las arreglará para interrumpir a una tarea que está utilizando al procesador?.

Esta es una pregunta que usted se hará sin lugar a dudas, y es una de las cosas que siempre veo quedan sin explicar adecuadamente cuando se presenta un sistema operativo.

El scheduller está compuesto de dos elementos esenciales para su funcionamiento:

El código que actualiza los TCB de las tareas y que además determina qué tarea entrará en ejecución.
Una Interrupción que permite al scheduller entrar en contexto.

El segundo punto nos da la clave para comprender el mecanismo que permite al scheduller interrumpir la ejecución de cualquier tarea que se encuentra ejecutando código. Una vez lanzada la interrupción, se transfiere el control del procesador al código principal del scheduller y este después de actualizar los TCB determinará qué tarea debe obtener el procesador.

Analizando lo anterior nos damos cuenta que el scheduller consume tiempo de procesamiento como cualquier tarea, pero eso no debe preocuparnos demasiado. Si este código está bien diseñado y usted ha escogido el procesador adecuado o lo ha configurado para que corra a la velocidad correcta, su carga sobre el sistema no debe superar el 2 % del tiempo total disponible de procesamiento.

Métodos de planificación de tareas

Ya sabemos los elementos básicos que le permiten al scheduller entrar en contexto, pero llegado el momento de decidir que tarea obtendrá el procesador es cosa difícil. Por ello han aparecido una buena cantidad de métodos de planificación. Y en muchos casos se combinan varios de ellos para lograr un sistema con ciertas características de desempeño.

Atendiendo a la forma en que se implementan, estos métodos se pueden clasificar en dos grandes grupos:

Decisión mediante una política establecida sólo en el código del planificador.
Decisión mediante una política establecida en la implementación de la aplicación.

Usted puede dejar que el planificador decida por sí solo, hay varios tipos de métodos de decisión en este caso, como por ejemplo:
  • Tiempo menor para completar la ejecución.
  • Tiempo mayor para completar la ejecución.
  • Lista circular (Round-Robin).
  • La que lleva el mayor tiempo sin ejecutar.
El problema con todas estas políticas de planificación está en que deja fuera la habilidad del programador para influir en el mecanismo de decisión de cuales tareas son más o menos importantes y eso puede afectar seriamente el desempeño del sistema. Dentro de las políticas de planificación, en la implementación de la aplicación, la asignación de prioridades es, sin lugar a dudas, la más utilizada por ser simple y efectiva.

FreeRTOS utiliza este método de planificación en el primer nivel de decisión, por lo que el equipo de FreeRTOS cosidera que usted, desarrollador, es el que sabe que debe ir primero o después.

Si FreeRTOS utilizara únicamente el mecanismo de asignación de prioridades, estaría obligado a tener una sola tarea en cada nivel de prioridades, y por consiguiente, deberíamos asignar una prioridad única a cada tarea. Este método es ampliamente utilizado cuando se puede determinar la prioridad de cada tarea, pero no es práctico en muchos casos, dado que muchas tareas pueden compartir el mismo nivel de prioridad en un sistema. FreeRTOS utiliza una combinación de políticas de planificación, la primera es la asignación de prioridad, y la segunda es el método Round-Robin para las tareas que comparten la misma prioridad.

Con esta combinación se consigue un adecuado balance entre la experiencia del desarrollador y la repartición justa del procesador entre tareas que tienen el mismo nivel de importancia. Resuelto el problema de repartir el procesador entre tareas que están listas para ejecutarse podríamos preguntarnos

¿y que pasa cuando una tarea decide que debe esperar por un evento o recurso?.

En estos casos la tarea hace una llamada explícita al scheduller a través de su API, el scheduller entra en contexto y realiza su trabajo otorgando el procesador a la tarea que le corresponde en ese momento. Un método similar se sigue cuando una Interrupción entra en contexto y gracias a ello una tarea que estaba esperando por el evento vinculado a la Interrupción debe comenzar a ejecutar código.

Todos estos casos serán expuestos mediante ejemplos prácticos posteriormente.


Marcapasos

Como habíamos visto el scheduller debe entrar en contexto, cuando una tarea se ha quedado con el procesador pero este debe ser entregado a otra tarea. Eso está claro, pero:
¿cuando es el momento de hacer que el scheduller entre en su contexto?

Para lograrlo podríamos pensar en un buen número de métodos, sin embargo, existe uno muy simple y eficaz. Si hacemos que el sistema obligue al scheduller a ejecutar código a intervalos regulares de tiempo, podremos asegurar que nadie se apodere del procesador por tiempo indefinido. Para lograr lo anterior basta con asociar la Interrupción de un temporizador con el mecanismo que asegura la entrada en contexto del scheduller. En el caso de los puertos de FreeRTOS para microcontroladores PIC, lo anterior se logra reservando un temporizador para esta función, de modo que usted obtiene con ello una especie de “marca pasos“ para su sistema y con ello garantiza la entrada del scheduller en contexto cada cierto tiempo.

Si usted es de los que gusta de utilizar el TIMER1 de sus microcontroladores, puede ir renunciando a esa práctica, ya que en todos los casos este temporizador está dedicado a garantizar el “marca pasos” del sistema.

Podría parecernos que sacrificar un temporizador para que FreeRTOS pueda hacer su trabajo es demasiado, pero yo puedo asegurarle que es un sacrificio que bien vale la pena y se lo demostraré posteriormente.

Hago este comentario porque si usted es de los que gusta echar mano al TIMER1 le sugiero replanificar su estrategia de uso de temporizadores.

lunes, 7 de abril de 2014

Qué significa sistema operativo a tiempo Real?

El tiempo real es un término difícil de definir y en el caso de los sistemas operativos no existe concenso respecto de una definición. Sin embargo, existe un criterio bastente generalizado que considera que un sistema operativo es de tiempo real si puede responder adecuadamente a los estímulos a que es sometido.

Aplicando este criterio a cualquier sistema operativo podríamos pensar que tiempo real significa responder a los estímulos lo más rápido posible. Sin embargo, esto no es del todo cierto ya que al poner esta definición en el contexto de algunas aplicaciones comunes podríamos encontrar matices.


Por ejemplo, un procesador que controla el sistema de frenos de un vehículo debe ser capaz de responder al estímulo del conductor o del sistema anticolisión en una ventana de tiempo muy bien definida. Si el sistema falla en su respuesta al estímulo inicial y en completar las acciones requeridas, no será de tiempo real. Además, fallar implica tener como resultado un escenario de catástrofe.

Como conclusión, este es un sistema de tiempo real estricto, ya que no se permite ni una respuesta tardía ni un completamiento de las acciones derivadas fuera de tiempo.

Por otro lado, tomemos como ejemplo, al procesador de un reproductor personal de MP3. Este también debe ser un sistema de tiempo real, ya que la lectura y decodificación de archivos MP3 debe ser realizada en plazos de tiempo que permitan que los datos sean convertidos de formato digital a analógico según un orden y frecuencias establecidos. Pero si por alguna razón el sistema falla en cumplir con estas exigencias; el peor escenario sería causarle molestias a la persona que escucha música. A este tipo de sistemas de tiempo real se les conoce como de tiempo real no estricto.

Ahora podríamos pensar que si un sistema es capaz de responder adecuadamente a los estímulos será de tiempo real. Esta conclusión tampoco es cierta, ya que un RTOS debe garantizar otros requisitos que lo hagan merecedor de esta calificación. Explicar cuáles son esos requisitos está fuera de los objetivos de este Blog, pero si usted está interesado en ellos, puede consultar la abundante literatura que existe al respecto y aclarar sus dudas.

Por último, podríamos pensar que si un sistema que utiliza un sistema operativo de tiempo real no puede responder adecuadamente a los estímulos, entonces el sistema operativo no puede ostentar su título. Esto es cierto para la aplicación pero no se aplica por consecuencia al RTOS. Puede que el RTOS esté bien calificado pero su sistema esté mal diseñado, o los desarrolladores hayan fallado en utilizarlo correctamente, quizá se ha seleccionado un procesador no adecuado o podría existir cualquier otra razón que impida que el sistema no cumpla la condición para el “tiempo real”, aún cuando se utilice un RTOS.


FreeRTOS como varios OS, está clasificado como RTOS y por su diseño puede asumir tareas de tiempo real estricto. Sin embargo, eso no quiere decir que sólo con utilizarlo ya tendremos un sistema de tiempo real, para ello es necesario dominar ciertas técnicas de diseño de hardware y firmware.

Como conclusión podríamos señalar que este es un campo difuso, donde no hay un concenso establecido en cuanto a definiciones, razones por las cuales existe un intenso debate en los aspectos filosóficos ligados a estos temas. El autor pretende dejar a un lado las discusiones de este tipo y concentrarse en mostrar una técnica para el desarrollo de aplicaciones con FreeRTOS lo más depurada y precisa que pueda, con el interés de que los estudiantes puedan alcanzar los conocimientos y habilidades que les permitan obtener aplicaciones de tiempo real.

Tomado de Curso de FreeRTOS por Ing. Reinier Torres Labrada.
(http://www.micropic.es/cursos/file.php/3/clases/C01_Introduccion.pdf)

miércoles, 2 de abril de 2014

Por qué usar RTOS en Micros? RTuinOS vs Arduino.

Arduino ( www.arduino.cc ) es una plataforma que integra hardware y software abierto con controladores para diversos fines, en donde el principal objetivo es disminuir el tiempo de diseño y proyectos, así como cuenta con un grupo desarrollador en donde actualiza casi bimensualmente los drives y ejemplos.  

Arduino es un concepto de diseño que viene junto con un sencillo IDE para ser usado en el desarrollo integral de proyecto empleando Micros, que contiene una cantidad de herramientas completas para escribir código fuente tanto en amsembler y C. 



Con el IDE de desarrollo puedes navegar a través de muestras de ejemplo y bibliotecas, para compilar y enlazar el software
y subirlo al bloque memoria y flashear (cargarla en el micro). 

El proyecto RTuinOS (pagina oficial RTuinOS) añade al proyecto Arduino un nuevo paradigma en la programación de un sistema operativo en tiempo real(claro con su limitaciones sobre todo en Timers).

Los Sistemas operativos en tiempo real, o RTOS, simplifican y le da robustez a las aplicaciones de programación de bucle extendidos o programacion procedimental, ya que existe un control de entradas y salidas en donde se establecen en consecuencia todos "procesos" en fracciones de tiempo exacto (es allí donde nace la necesidad de quitar un timer del micro). Por ejemplo, el controlador de temperatura para un instalación de calefacción se podría diseñar de esta manera:


Sensores de temperatura, que informan de las temperaturas ambientales que son evaluadas en un proceso de control y el proceso del quemador y tal vez algunas válvulas que controlan para producir la temperatura objetivo deseada. Dichos procesos corren de manera "Pseudo Paralela".



Además , el uso de un sistema en tiempo real el programa podría casualmente actualizar periódicamente un proceso llamado pantalla que espera un aviso o mensaje de otro proceso para dar información. Es allí la necesidad de diferenciar la Programación regular, se evalua el proceso el tiempo que dure y hasta que no termine no pasa al otro proceso en un esquema de bucles, mediante esta programación el micro no optimiza los delays de proceso por lo que el micro se queda haciendo tiempo sin uso importante; las librerías de Arduino son procedimientos que debn finalizar para dar paso a otros. Mientras que el uso de Sistemas operativos en tiempo real se caracterizar por el uso profesional de micro controladores en tiempos y procedimientos con su optimización tácita.


RTuinOS, es un pequeño sistema en tiempo real de funcionamiento ( RTOS ) para la plataforma Arduino, es fácil de usar y se ajusta bien a las librerías y código del Arduino, evidentemente se añade el concepto de hilos o "procesos" de ejecución pseudo paraleloa la aplicación. Pensar en OS sobre Arduino es pensar en procesos corriendo a la vez en donde cada proceso se comunican entre sí para llegar a un fin.


El esquema tradicional Arduino tiene dos puntos de entrada; la configuración de la función, que es el lugar para poner el código de inicialización necesario para ejecutar elbosquejo y la función de bucle, que se llama periódicamente; empleando el esquema de PLC equipo. La frecuencia de losbucle no es determinista, pero depende del tiempo de ejecución del código dentro del bucle (problema del esquema Arduino).



Usando RTuinOS, las dos funciones mencionadas continúan existiendo y continuan teniendo el mismo significado. Sin embargo, como parte de la inicialización de código en
configuración se puede definir una serie de tareas que tienen propiedades individuales (tiempo de cronogramas por procedimiento), la propiedad más relevante de una tarea es una función de código C, que se convierte en el
llamada a la función de la tarea. Una vez que entra en el bucle tradicional Arduino, todo estas funciones de trabajo se ejecutan en paralelo entre sí y la ejecución repetida de bucle de la función. Decimos, que un bucle se convierte en la tarea ociosa de el RTOS, de hecho un delay es una tarea ociosa tambien para el micro y el lo optimiza.


Una característica de RTuinOS es que el comportamiento de una tarea no es totalmente predeterminada en tiempo de compilación. RTuinOS apoya a regular y controla por tiempo la tareas, así como el cronograma de tareas. Las tareas pueden ser preventivas ocomportarse cooperativamente. La programación de tareas se puede hacer usando intervalos de tiempo y un patrón de round robin. Por otra parte, muchos de estos modos se pueden mezclar para lograr aun mas mejoras en el sistema a ser escalable. Una tarea esno por sí regular, su código implementar decide lo que pasa y esto puede decidirse en el contexto o situación dependiente. Esta flexibilidad es alcanzada por la idea básica de que tiene un planificador controlador de eventos, donde los casos de uso típico en RTOS son apoyados por proporcionar eventos de acuerdo a su uso. En muchas implementaciones de RTOS la característica, básica de una tarea está determinada por el tiempo de compilación, en RTuinOS esto se hace en parte con el tiempo de compilación y en parte con el tiempo de ejecución.RTuinOS se ofrece como un solo archivo de código fuente que se compila junto con su otro código, que ahora se convierte en una aplicación RTuinOS. En el caso más sencillo, solo basta con definir cualquier tarea, su aplicación
se parecen mucho a un esquema tradicional : se implementa la configuración y su función de bucle, el primero se llevará a cabo una vez al principio y al este último en varias ocasiones. RTuinOS por sí sola no puede ser compilado, es necesario que haya una aplicación. RTuinOS está organizado como un paquete que combina el archivo de origen RTuinOS con algunas aplicaciones de ejemplo que son los casos de prueba al mismo tiempo.El código fuente de cada aplicación de la muestra se mantiene en una carpeta separada, llamado tc <nn>. Cualquiera de ellos puede ser seleccionado para la compilación. Puede añadir más carpetas, manteniendo el código fuente de sus aplicaciones RTuinOS.