-
Notifications
You must be signed in to change notification settings - Fork 0
Cap 05 ‐ Semáforos para Sincronização e Acesso a Recursos
Condições de corrida acontecem quando duas ou mais tarefas competem pelo acesso a recursos que são compartilhados entre si. Aquele que o acessa primeiro vence a corrida, mas impede a execução correta das tarefas seguintes. É necessário a utilização de mecanismos de sincronização, como mutexes e semáforos, para controlar o acesso de recursos e sincronizar processos concorrentes.
Um Mutex (de Mutual Exclusion) funciona como uma "chave" ou um "token de permissão" para uma seção crítica.
- Apenas uma tarefa pode "possuir" o mutex por vez.
- Se uma tarefa quer entrar na seção crítica, ela deve primeiro "pegar" (take) o mutex.
- Se o mutex já estiver em posse de outra tarefa, a tarefa que tentou pegá-lo será bloqueada (colocada em uma fila de espera) até que o mutex seja "devolvido" (given).
- Ao sair da seção crítica, a tarefa deve devolver o mutex para que outras possam usá-lo
Você cria um mutex usando a função xSemaphoreCreateMutex().
#include "semphr.h" // Cabeçalho necessário
// Declara uma variável para guardar o handle do mutex
SemaphoreHandle_t xMeuMutex;
void setup() {
// Cria o mutex antes de iniciar o escalonador
xMeuMutex = xSemaphoreCreateMutex();
if (xMeuMutex != NULL) {
// Mutex criado com sucesso
}
}
As duas funções principais para interagir com um mutex são xSemaphoreTake() e xSemaphoreGive().
xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)
-
xSemaphore: O handle do mutex que você quer pegar. -
xTicksToWait: O tempo máximo que a tarefa deve esperar (bloquear) se o mutex não estiver disponível. - Usar
portMAX_DELAYfará com que a tarefa espere indefinidamente, o que é o comportamento mais comum para mutexes. - Retorno: Retorna
pdPASS(oupdTRUE) se o mutex foi pego com sucesso. RetornapdFAIL(oupdFALSE) se o tempo de espera (xTicksToWait) expirou antes do mutex se tornar disponível.
xSemaphoreGive(SemaphoreHandle_t xSemaphore)
-
xSemaphore: O handle do mutex que você está devolvendo. - Uma tarefa nunca deve devolver um mutex que ela não pegou.
Embora a API seja quase a mesma, a finalidade de um Semáforo Binário é diferente. Ele para sincronizar a execução entre tarefas ou entre uma tarefa e uma interrupção (ISR).
Pense nele como um sinalizador. Uma tarefa pode esperar por um evento (como um botão sendo pressionado ou um dado chegando pela serial) e só continuará a execução depois que outra tarefa ou uma ISR "sinalizar" que o evento ocorreu.
Para criar um semáforo, você usa a função xSemaphoreCreateBinary().
SemaphoreHandle_t xSemaforoEvento;
xSemaforoEvento = xSemaphoreCreateBinary();
Dica: Um semáforo binário é criado no estado "vazio" ou "pego". Você precisa explicitamente dar (give) o semáforo antes que qualquer tarefa possa pegá-lo (take).
// Assumindo que temos uma interrupção de pino configurada para um botão
SemaphoreHandle_t xBotaoPressionadoSemaforo;
// ESTA É A ROTINA DE INTERRUPÇÃO (ISR)
void isr_callback_botao() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 'Give' o semáforo para desbloquear a tarefa de processamento.
// Use a versão "FromISR" que é segura para interrupções.
xSemaphoreGiveFromISR(xBotaoPressionadoSemaforo, &xHigherPriorityTaskWoken);
// Se a tarefa desbloqueada tiver maior prioridade que a atual, força uma
// troca de contexto ao sair da ISR.
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// ESTA É A TAREFA QUE ESPERA PELO EVENTO
void tarefaProcessaBotao(void *p) {
for(;;) {
// A tarefa bloqueia aqui indefinidamente até a ISR dar o semáforo.
if (xSemaphoreTake(xBotaoPressionadoSemaforo, portMAX_DELAY) == pdPASS) {
// A ISR sinalizou!
printf("Botão foi pressionado! Processando...\n");
// ... faz o processamento demorado aqui ...
}
}
}
void main() {
// ...
xBotaoPressionadoSemaforo = xSemaphoreCreateBinary();
// ... configura a interrupção do pino para chamar isr_callback_botao() ...
xTaskCreate(tarefaProcessaBotao, "TaskBotao", 256, NULL, 2, NULL); // Prioridade maior
vTaskStartScheduler();
}
O Kernel do FreeRTOS adiciona tarefas na fila de espera quando uma tarefa chama xSemaphoreTake() e o semáforo (ou mutex) não está disponível. O parâmetro xTicksToWait controla este comportamento.
-
xTicksToWait = 0: A tarefa não será bloqueada. A função verifica o semáforo uma vez. Se estiver disponível, ela o pega e retorna pdPASS. Se não, ela retorna pdFAIL imediatamente e a tarefa continua sua execução. Útil para "tentar pegar" (polling). -xTicksToWait > 0(e< portMAX_DELAY): A tarefa será colocada em uma fila de espera específica para aquele semáforo e entrará no estado Bloqueado. Ela não consumirá nenhum tempo de CPU. Se o semáforo for devolvido por outra tarefa antes que o tempoxTicksToWaitexpire, a tarefa é movida para o estadoPronto, pega o semáforo e continua a execução. Se o tempo expirar, a tarefa também é movida para o estadoPronto, masxSemaphoreTake()retornapdFAIL. -
xTicksToWait=portMAX_DELAY: Igual ao anterior, mas a tarefa esperará na fila indefinidamente, sem timeout.
Quando xSemaphoreGive() é chamado e há tarefas na fila de espera daquele semáforo, o FreeRTOS automaticamente remove a tarefa de maior prioridade da fila (ou a que esperou por mais tempo, se as prioridades forem iguais) e a move para o estado "Pronto".