Está en la página 1de 33

Sincronización entre Tareas

• Al usar un RTOS, se recomienda que las tareas se


comuniquen entre si con llamadas al sistema del RTOS.

• En el caso de que una tarea esté esperando por ejemplo que


se presione una tecla, la tarea puede quedar bloqueada
hasta que se produzca el evento (el evento es en este caso
será la acción de presionar la tecla).

• Para ello existe el concepto de “Semáforo”.


Que es un semáforo?
• Un semáforo restringe el acceso a una sección particular del
programa (de la misma forma que un semáforo vial no permite
el cruzar la calle si están pasando autos).

• En caso que restrinja el acceso a un determinado recurso, el


uso del semáforo será una convención a la que se deben
adherir tolas las tareas que requieran acceso a ese recurso.

• La creación de un semáforo consume memoria del sistema.


Además cuando se produce un cambio en el semáforo, el
scheduler verifica si es necesario realizar una cambio de
contexto, lo cual implica un uso de la CPU.
Semáforos en RTOS
• Los semáforos tienen dos operaciones asociadas:

o Tomar el semáforo: es equivalente a que el semáforo


pase al color rojo.

o Liberar el semáforo: es equivalente a que pase a verde.

• En el caso de que el semáforo se puede tomar o liberar una vez,


se llama “semáforo binario”.
• Nunca tomar un semáforo en una interrupción.
Semáforos en FreeRTOS
• Para crear un semáforo con el CMSIS-RTOS de CubeMX se usa:
osSemaphoreDef(BinarySem01);
BinarySem01Handle =
osSemaphoreCreate( osSemaphore ( BinarySem01 ), 1 );

• Donde el valor numérico 1 en osSemaphoreCreate, indica que


es un semáforo binario.
Semáforos en FreeRTOS
• Se pueden crear desde la interface gráfica de CubeMX
Semáforos en FreeRTOS
• Al presionar “Add” se abre otra ventana
Semáforos en FreeRTOS
• Para tomar un semáforo con el CMSIS-RTOS de CubeMX se usa:
int32_t osSemaphoreWait (osSemaphoreId semaphore_id,
uint32_t millisec);

• Se debe especificar la identificación del semáforo (por ej.:


BinarySem01) y luego el tiempo máximo de bloqueo en
milisegundos.
• Si se realizó la operación con éxito, devuelve “osOK”. Si expiró el
tiempo máximo de bloqueo devuelve “osErrorOS”. Si el id del
semáforo es incorrecto devuelve “osErrorParameter”.
Semáforos en FreeRTOS
• Para liberar un semáforo con el CMSIS-RTOS de CubeMX se usa:
osStatus osSemaphoreRelease (osSemaphoreId
semaphore_id)

• Se debe especificar la identificación del semáforo.


• Si se realizó la operación con éxito, devuelve “osOK”. En otro caso
devuelve “osErrorOS”.

• La función tiene en cuenta si se libera el semáforo desde una


interrupción.
Semáforos en FreeRTOS
• Si el semáforo se usará para proteger el acceso a un recurso (sección
crítica), se lo debe tomar con tiempo de retorno máximo finito. Ej.:
if (osSemaphoreWait(BinarySem01, 10000) ==osOK){
---//acceso al recurso
osSemaphoreRelease(BinarySem01); //Liberar semáforo
}

• Todas las funciones de FreeRTOS que bloquean a la tarea que la


llaman tienen un tiempo de bloqueo máximo. Se debe verificar que
se realizo el acceso al semáforo o sino si expiro el tiempo.
Semáforos en FreeRTOS
• Si el semáforo se usará esperar una evento, se lo puede esperar en
forma indefinida. Ej.:
if (osSemaphoreWait(BinarySem01, osWaitForever) ==osOK){
---//acción que espera que se produzca el evento.
}

void Interrup_ISR(void){//rutina de interrupción


osSemaphoreRelease(BinarySem01);
}
• El valor de osWaitForever es el máximo que permite el RTOS.
Implementación de Semáforos
• Los semáforos son objetos del kernel, se deben usar con
racionalidad, ocupan memoria y tiempo de CPU.

• El scheduler debe verificar si se realiza una cambio de contexto cada


vez que se opera sobre un semáforo. Si el manejo del evento es
inmediato o diferido dependerá de las prioridades de las tareas.

• Deben ser vistos por todo el sistema, se crean en forma global.

• Los semáforos son útiles si hay dependencias entre tareas y eventos


o entre tareas solamente.
Ejemplo: diagrama de ejecución
Exclusión entre Tareas
• Cuando varias tareas que se ejecutan en forma concurrente
quieren acceder al mismo recurso, entrarán en conflicto.
Pero no se accederá en forma simultánea, sino que habrá en
cambio de contexto entre que se empieza y termina de
modificar el recurso.

• El problema se evita turnando el acceso al recurso. Esto se


llama exclusión mutua y se tienen varias estrategias:
Exclusión: Alternativa 1
• Se deshabilitan las interrupciones, no puede ocurrir ningún
cambio de contexto.
• Se debe tener en cuenta que el sistema se vuelve
cooperativo y no se atiende ningún evento.
• FreeRTOS provee las macros taskENTER_CRITICAL() y
taskEXIT_CRITICAL(). Su uso debe ser apareado (un enter es
seguido de un exit).
Exclusión: Alternativa 2
• Suspender el scheduler: se usa la función
osThreadSuspendAll(); la cual suspende a todas la
tareas restantes, pero mantiene las interrupciones activas.
• Se eliminan los cambios de contexto durante el acceso al
recurso. Luego se reanuda el scheduler con
osThreadResumeAll();.
• Se debe tener cuidado de que todos los eventos pendientes,
serán atendidos inmediatamente después de la reactivación
(semáforos, etc.).
Exclusión: Alternativa 3
• Subir la prioridad de la tarea: FreeRTOS permite modificar la
prioridad de las tareas.
• Al aumentar la prioridad de la tarea que accede al recurso,
no se realizará el cambio de contexto.
• De esta forma se pueden tener interrupciones habilitadas y
atención de los eventos y al mismo tiempo definir un umbral
de tareas que no competirán con la tarea crítica.
Exclusión: Alternativa 4
• Usar un mutex del sistema: FreeRTOS provee un elemento
que es similar a un semáforo, pero con soporte para
exclusión mutua.
• Es una diferencia conceptual. Cuando varias tareas quieren
usar un recurso, se resguarda al mismo con un mutex. Se
puede interpretar como una llave de acceso al recurso.
• La única tarea que puede accederlo es la que tiene tomado
el mutex.
• Es imprescindible que al terminar de usar el recurso, se
libere el mutex.
Mutex en FreeRTOS
• Posee las mismas funciones que el semáforo:

• Para crear un mutex con el CMSIS-RTOS de CubeMX se usa:


Mutex01Handle = osMutexCreate(osMutex(Mutex01));

• Para tomar un mutex:


osStatus osMutexWait (osMutexId mutex_id, uint32_t
millisec)

• Para liberar un mutex:


osStatus osMutexRelease (osMutexId mutex_id)
Mutex en FreeRTOS
• Se puede crear un mutex desde la interface gráfica de CubeMX
Mutex en FreeRTOS
• Al presionar “Add” se abre otra ventana
Implementación de Mutex
• Cuando se usan mutex en FreeRTOS se realiza un manejo distinto al
semáforo, para considerar el caso de “inversión de prioridades”.
• Ejemplo:
Una tarea de alta prioridad está bloqueada.
Mientras, una tarea de menor prioridad que la anterior toma un mutex
para acceder a un recurso.
Luego la tarea de mayor prioridad pasa al estado Ready e intenta acceder al
recurso.
• En este caso, la tarea de alta prioridad debe esperar a que se libere el
mutex.
Implementación de Mutex
Ahora una tarea de prioridad intermedia, pasa al estado Running
durante un tiempo.
• Como resultado la tarea de prioridad alta esta esperando a la tarea
de prioridad menor (por eso es inversión de prioridad), pero esta
última no liberará el mutex porque no recibe tiempo de ejecución.

Para evitar esto, el mutex en FreeRTOS incorpora un método


llamado “priority inheritance”.
• Cuando ocurre una inversión de prioridades, el scheduler eleva la prioridad
de la tarea que tiene el mutex al nivel de la tarea que tiene mayor prioridad.
• Esto es una excepción al mecanismo “Fixed priority preemptive
scheduling”.
Intercambio de datos entre Tareas (Queue)
• Las tareas son funciones en C, que cuando dejan de ser
ejecutadas, las referencias a su pila dejan de ser válidas.

• Queda como alternativa usar variables globales para


intercambiar datos. Pero se tiene el problema:
• Cuidar el acceso al recurso.
• Las tareas que se ejecutan aperiódicamente esperan una
lectura/escritura de un dato para procesar.

• Se necesita un sistema de comunicación de datos


sincronizado.
Intercambio de datos entre Tareas
• Para modularizar la implementación del sistema, se suele
separar las tareas en dos grupos:
• Una tarea produce datos.
• Otra los consume (si no tiene datos para consumir se bloquea).

• Si se producen mas datos de los que se consumen se


necesita un buffer o un almacenamiento temporal de los
mismos. Si el buffer se llena, la tarea productora se bloquea.
• La forma mas simple de solucionarlo es usando pilas tipo
FIFO o “Queue”.
Intercambio de datos entre Tareas
• El OS provee las funciones para operar sobre las Queue.
Permite crearla, introducir datos o sacar datos.
• Las Queue se crean en el “Heap” del sistema. Ocupan
recursos.
• Permite trabajar con cualquier tipo de datos, no solo los con
que se creo.
• Se crean antes de iniciar el scheduler.
• Es importante estimar la cantidad de elementos máximos en
la Queue, luego no se puede modificar este parámetro.
Queue FIFO en FreeRTOS
• Para crear una Queue con el CMSIS-RTOS de CubeMX se usa:
Queue01Handle = osMessageCreate(osMessageQ(Queue01));

• Para leer un dato de la Queue se usa:


osEvent osMessageGet (osMessageQId queue_id, uint32_t
millisec)
• Tiene un tiempo máximo de espera por el dato en
milisegundos. En caso de que la lectura se realice con éxito
osEvent.status será osEventMessage y el dato
estará en osEvent.value . En caso de time-out
osEvent.status será osEventTimeout.
Queue FIFO en FreeRTOS
• Para escribir un dato en la Queue se usa:

osStatus osMessagePut (osMessageQId queue_id, uint32_t


info, uint32_t millisec)

• Tiene un tiempo máximo de espera por el dato en


milisegundos. En caso de que la operación se realice con
éxito osStatus será osOK. En otro caso osStatus será
osErrorOS.
Queue FIFO en FreeRTOS
• Para saber cuantos datos hay en la Queue se usa:

uint32_t osMessageWaiting(osMessageQId queue_id)

• Retorna la cantidad de elementos en la Queue.


• Es importante que antes de leer un dato de la Queue se
verifique que tenga datos. En caso contrario la lectura será
bloqueante.
• Los semáforos y mutex se implementan con Queue de un
único elemento.
Queue en FreeRTOS
• Se puede crear una Queue desde la interface gráfica de CubeMX
Queue en FreeRTOS
• Al presionar “Add” se abre otra ventana
¿Por qué usar un RTOS?
• Para cumplir con compromisos temporales estrictos.
• El RTOS ofrece funcionalidad para asegurar que una vez ocurrido
un evento, la respuesta ocurra dentro de un tiempo acotado. Es
importante aclarar que esto no lo hace por sí solo sino que brinda
al programador herramientas para hacerlo de manera más sencilla
que si no hubiera un RTOS (esto implica que una aplicación mal
diseñada puede fallar en la atención de eventos aún cuando se use
un RTOS).
• Para no tener que manejar el tiempo “a mano”
• El RTOS absorbe el manejo de temporizadores y esperas, de modo
que hace más facil al programador el manejo del tiempo.
¿Por qué usar un RTOS?
• Multitarea.
• Simplifica sobremanera la programación de sistemas con varias
tareas.
• Escalabalidad
• Al tener ejecución concurrente de tareas se pueden agregar las
que haga falta, teniendo el único cuidado de insertarlas
correctamente en el esquema de ejecución del sistema.
• Mayor reutilizabilidad del código
• Si las tareas se diseñan bien (con pocas o ninguna dependencia) es
más fácil incorporarlas a otras aplicaciones.
Contras de un RTOS
• Se gasta tiempo del CPU en determinar en todo momento
qué tarea debe estar corriendo. Si el sistema debe manejar
eventos que ocurren demasiado rápido tal vez no haya
tiempo para esto.
• Se gasta tiempo del CPU cada vez que debe cambiarse la
tarea en ejecución.
• Se gasta memoria de código para implementar la
funcionalidad del RTOS.
• Se gasta memoria de datos en mantener una pila y un TCB
(bloque de control de tarea) por cada tarea.

También podría gustarte