Sistemas Operacionais

FreeRTOS – Temporizadores de Software

Eletrogate 30 de março de 2022

Introdução

Olá, caro leitor! Tudo bem? No último artigo publicado, apresentamos mecanismos de sincronização e instrumentos para trabalhar com recursos compartilhados entre processos. Neste artigo, será apresentada a ferramenta Software Timer (temporizadores de software).


Software Timer

Em poucas palavras, “Software Timer” é um temporizador que permite que uma determinada função seja executada em certo tempo estabelecido no futuro. A função executada pelo “Software Timer” é chamada de função de retorno do temporizador (timer’s callback function). O intervalo de tempo entre o início do temporizador e o momento em que a função de retorno é executada é chamado de “período do cronômetro” (timer period).

O Software Timer disponível no FreeRTOS permite dois modos de operação:  

  • One-shot (disparo único) – A função de retorno do temporizador é executada apenas uma vez. Ele não reinicia automaticamente, deve ser reiniciado explicitamente pelo usuário.
  • Auto-reload (recarga automática) – O temporizador se reinicia automaticamente depois que a função de retorno é executada. Isso resulta na execução periódica da função de retorno.

As funções de retorno de chamada do temporizador são executadas no contexto da tarefa de serviço do “Software Timer”. Portanto, é essencial que as funções de retorno do temporizador nunca tentem o bloquear. Por exemplo, uma função de retorno não deve chamar as API’s vTaskDelay(), vTaskDelayUntil() ou especificar um tempo de bloqueio diferente de zero ao acessar uma fila ou um semáforo.


Criando Software Timer

Conforme citado nos artigos anteriores, o FreeRTOS contém duas abordagem para manipular a memória RAM. A primeira, utilizando o método de alocação da memória RAM dinâmica, a partir do Heap e a segunda, por alocação de memória RAM estática em tempo de compilação. Para entender melhor a diferença entre alocação de memória estática vs dinâmica consulte o link. Para esse e os demais artigos, vamos explicar as funções do FreeRTOS que trabalham com alocação de memória RAM dinâmica.

Como para qualquer outro objeto do FreeRTOS, é necessário, para o uso deste recurso, declarar uma variável. Para o Software Timer, o tipo é TimerHandle_t. Essa variável será utilizada como o identificador do temporizador.

Protótipo da função

TimerHandle_t xTimerCreate( const char * const pcTimerName,
                   const TickType_t xTimerPeriod,
                   const UBaseType_t uxAutoReload,
                   void * const pvTimerID,
                   TimerCallbackFunction_t pxCallbackFunction );

Parâmetros: 

  • pcTimerName: Uma string com o nome de Software Timer. Esse item é usado principalmente para facilitar a depuração.
  • xTimerPeriod: O valor do período do temporizador. O valor é especificado em ticks, a macro pdMS_TO_TICKS(x) pode ser utilizada para converter o valor do tempo em milissegundos para o valor em ticks do sistema. O período do temporizador deve ser maior que 0.
  • uxAutoReload: Para determinar o Auto-reload (recarga automática) deve atribuir o valor pdTRUE. Para configurar como One-shot (disparo único) o valor deve ser igual a pdFALSE
  • pvTimerID: Ponteiro para obter o ID do software timer a ser criado.     
  • pxCallbackFunction: Função de retorno a ser executada quando o temporizador expirar. A função de retorno deve conter o protótipo definido por TimerCallbackFunction_t, que é:
void vCallbackFunction( TimerHandle_t xTimer );

Retorno:

Se o temporizador foi criado com sucesso, o identificador para o “Software Timer” será retornado. Caso ocorra alguma falha será retornado o valor NULL.


Iniciar Software Timer

Como já visto nos artigos anteriores, existem duas tratativas distintas na utilização das API’s do FreeRTOS, funções especializadas e funções genéricas. O “Software Timer” obedece a essa regra. Quando o início do temporizador for feito a partir de uma tarefa, deve-se usar a função genérica. Se o início do Software Timer for a partir de uma rotina de interrupção ou de uma função de “callback” é recomendado utilizar a API especializada.

xTimerStart é a função genérica para iniciar o temporizador.

Protótipo da função

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xBlockTime );

Parâmetros:

  • xTimer: O identificador do Software Timer que está sendo iniciado/reiniciado.
  • xBlockTime: Especifica o tempo, em ticks, para que a tarefa de chamada seja mantida no estado Bloqueado, aguardando que o comando de parada seja enviado com êxito. A macro pdMS_TO_TICKS(x) pode ser utilizada para converter o valor do tempo em milissegundos. 

Retorno

O valor pdPASS será retornado quando o Software Timer iniciado com sucesso. Se o valor for igual a pdFAIL, significa que ocorreu alguma falha em iniciar o temporizador.

xTimerStartFromISR é a função especializada para iniciar o software timer.

Protótipo da função

BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

Parâmetros: 

  • xTimer: O identificador do Software Timer que está sendo iniciado/reiniciado. 
  • pxHigherPriorityTaskWoken: Se esse parâmetro for definido como pdTRUE, uma troca de contexto deve ser solicitada ao kernel antes que a interrupção seja encerrada. 

Retorno: 

O valor pdPASS será retornado quando o Software Timer iniciado com sucesso. Se o valor for igual a pdFAIL, significa que ocorreu alguma falha em iniciar o temporizador.


Interrompendo o Software Timer

Uma vez inicializado o temporizador, podemos o interromper. Como visto, para iniciar o “Software Timer”, o  FreeRTOS possui duas abordagens, oferecendo funções genéricas e especializadas. Para interromper o “Software Timer” é a mesma trativa, função genérica para ser utilizada a partir de uma tarefa e API especializada para uso em interrupções e funções de callback. 

xTimerStop é a função genérica para interromper o temporizador.

Protótipo da função

BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );

Parâmetros: 

  • xTimer: O identificador do temporizador que está sendo parado/interrompido.
  • xBlockTime: Especifica o tempo, em ticks, -a macro pdMS_TO_TICKS(x) pode ser utilizada para converter o valor do tempo em milissegundos- para que a tarefa de chamada seja mantida no estado Bloqueado, aguardando que o comando de parada seja enviado com êxito.

Retorno: 

O valor pdPASS será retornado quando o “Software Timer” for parado/interrompido com sucesso. Se o valor for igual a pdFAIL, ocorreu alguma falha ao parar o temporizador.

xTimerStopFromISR é a função especializada para interromper o temporizador.

Protótipo da função

BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

Parâmetros: 

  • xTimer: O  identificador do temporizador que está sendo parado/interrompido.
  • pxHigherPriorityTaskWoken: Se esse parâmetro for definido como pdTRUE, uma troca de contexto deve ser solicitada ao kernel antes que a interrupção seja encerrada. 

Retorno: 

O valor pdPASS será retornado quando o Software Timer for parado com sucesso. Se o valor for igual a pdFAIL, significa que ocorreu alguma falha ao parar o temporizador.


Alterando o Período do Software Timer

Tão importante como iniciar e parar o temporizador, é alterar o período do Software Timer.

xTimerChangePeriod é a função genérica para alterar o período do temporizador.

Protótipo da função

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, TickType_t xNewPeriod, TickType_t xBlockTime );

Parâmetros: 

  • xTimer: O  identificador do Software Timer a ser alterado.
  • xNewPeriod: Novo período do software timer. Especifica o tempo em ticks. A macro pdMS_TO_TICKS(x) pode ser utilizada para converter o valor do tempo em milissegundos.
  • xBlockTime: Especifica o tempo, em ticks, para que a tarefa de chamada seja mantida no estado Bloqueado, aguardando que o comando de troca de período seja enviado com êxito.

Retorno:

O valor pdPASS será retornado quando o “Software Timer” tiver seu período alterado com sucesso. Se o valor for igual a pdFAIL, significa que ocorreu alguma falha na atualização deste valor.

xTimerChangePeriodFromISR é a função especializada para alterar o período do temporizador. 

Protótipo da função

BaseType_t xTimerChangePeriodFromISR ( TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t *pxHigherPriorityTaskWoken );

Parâmetros:

  • xTimer: O  identificador do Software Timer a ser alterado. 
  • xNewPeriod: Novo período do software timer. Especifica o tempo em ticks. A macro pdMS_TO_TICKS(x) pode ser utilizada para converter o valor do tempo em milissegundos. 
  • pxHigherPriorityTaskWoken: Se esse parâmetro for definido como pdTRUE, uma troca de contexto deve ser solicitada ao kernel antes que a interrupção seja encerrada.

Retorno: 

O valor pdPASS será retornado quando for alterado o período do Software Timer com sucesso. Se o valor for igual a pdFAIL quando ocorrer alguma falha ao altera-lo.


Excluindo Software Timer

Como qualquer objeto no FreeRTOS, o “Software Timer” pode ser deletado. Quando é excluído um “Software Timer”, é liberada toda a memória RAM alocada para ele. 

Protótipo da função

BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xBlockTime);

Parâmetro: 

  • xTimer: O identificador do semáforo que está sendo deletado. 
  • xBlockTime: Especifica o tempo, em ticks, para que a tarefa de chamada seja mantida no estado Bloqueado, aguardando que o comando de exclusão seja enviado com êxito.

Retorno: 

O valor pdPASS será retornado quando o “Software Timer” for deletado com sucesso. Se o valor for igual a pdFAIL quando ocorrer alguma falha ao deletar o temporizador.


Projeto de Demonstração

O projeto de demonstração é composto por algoritmos de detecção e medição da largura do pulso. A partir da leitura da largura do pulso, é acionado o buzzer com diferente frequência e tons sonoros.

O código fonte desenvolvido para o projeto conta com duas tarefas, a primeira é “Tarefa_Pulso”, responsável pela detecção e leitura da largura do pulso. Para realizar a medida da largura do pulso, foi criado um “Software Timer”. O algoritmo da “Tarefa_Pulso”, quando detecta a borda de descida, inicia o temporizador e, quando detecta a borda de subida, o interrompe. Então, realiza a leitura do pulso e publica em uma fila para a segunda tarefa.

A segunda tarefa é “Tarefa_Buzzer” que, por sua vez, aguarda a informação da largura do pulso por meio da fila. Quando recebe a informação, checa o tamanho da largura para decidir o período, frequência e o tom sonoro a ser executado. Para controlar o tempo de acionamento do buzzer, foi criado um segundo “Software Timer”. Uma vez verificado o comprimento do pulso, o buzzer é acionado por meio deste temporizador.

Quando o tempo do segundo “Software Timer” é expirado, a “Tarefa_Buzzer” é notificada, por meio de um semáforo binário, para realizar o desligamento do buzzer. 

A seguir, temos a figura com o circuito elétrico desenvolvido para o projeto de demonstração.

Materiais necessários para o projeto com Temporizadores de Software 

  1. Módulo WiFi ESP32s Bluetooth 38 pinos
  2. Resistor 10K
  3. Push Button (Chave Táctil)
  4. Transistor NPN BC548
  5. Buzzer Ativo 5V
  6. Protoboard
  7. Jumpers

Código fonte

/**
 * @file main.cpp
 * @author Evandro Teixeira
 * @brief 
 * @version 0.1
 * @date 26-02-2022
 * 
 * @copyright Copyright (c) 2022
 * 
 */
#include <Arduino.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include "buzzer.hpp"

#define COLOR_BLACK         "\e[0;30m"
#define COLOR_RED           "\e[0;31m"
#define COLOR_GREEN         "\e[0;32m"
#define COLOR_YELLOW        "\e[0;33m"
#define COLOR_BLUE          "\e[0;34m"
#define COLOR_PURPLE        "\e[0;35m"
#define COLOR_CYAN          "\e[0;36m"
#define COLOR_WRITE         "\e[0;37m"
#define COLOR_RESET         "\e[0m"
#define BUZZER_FREQUENCY    30                  // Frequency 2000 Hz
#define BUZZER_DUTYCYCLE    75                  // Init DutyCycle 50%
#define BUZZER_CONFIG       (BuzzerPin_t){15,4} // Pin, Channel PWM
#define PIN_BUTTON          14                  // GPIO14 
#define LED_BOARD           2                   // Pino do LED
#define DEBOUNCE_BUTTON     50                  // Tempo do debounce do botão
#define SW_TIMER_PERIOD_PULSO    10             // 10 ms
#define SW_TIMER_PERIOD_BUZZER   500            // 500 ms
#define NUMBER_OF_ELEMENTS       8              // Número de elementos na fila

/**
 * @brief Cria objeto Buzzer
 * @return buzzer 
 */
buzzer Buzzer(BUZZER_CONFIG);

/**
 * @brief 
 */
SemaphoreHandle_t xSemaphore_BuzzerOff = NULL;
SemaphoreHandle_t xMutex_PulseTimerCounter = NULL;
TimerHandle_t TimerPulso;
TimerHandle_t TimerBuzzer;
QueueHandle_t QueuePulso;
uint32_t PulseTimerCounter;  


/**
 * @brief 
 * @param parameters 
 */
void Tarefa_Pulso(void *parameters);
void Tarefa_Buzzer(void *parameters);

uint32_t PulseTimerCounter_Get(void);
void PulseTimerCounter_Clear(void);
void PulseTimerCounter_Increment(void);

void Callback_TimerPulso(TimerHandle_t timer);
void Callback_TimerBuzzer(TimerHandle_t timer);

void setup() 
{
  // Inicializa a Serial 
  Serial.begin( 115200 );
  Serial.printf("\n\rFreeRTOS - Software Timer\n\r");

  // Inicializa o Buzzer 
  Buzzer.begin(BUZZER_FREQUENCY,BUZZER_DUTYCYCLE);
  Buzzer.stop();

  // Inicializa pino 14 como entrada
  pinMode(PIN_BUTTON, INPUT_PULLUP);

  // Inicializa pino do LED on Board
  pinMode(LED_BOARD,OUTPUT);
  digitalWrite(LED_BOARD,LOW);

  // Cria semafaro binario xSemaphore_Pulso
  vSemaphoreCreateBinary( xSemaphore_BuzzerOff );
  if(xSemaphore_BuzzerOff == NULL)
  {
    Serial.printf("\n\rFalha em criar o semafaro xSemaphore_BuzzerOff");
  }

  // Cria Mutex xMutex_PulseTimerCounter
  xMutex_PulseTimerCounter = xSemaphoreCreateMutex();
  if(xMutex_PulseTimerCounter == NULL)
  {
    Serial.printf("\n\rFalha em criar o Mutex para variavel global xMutex_PulseTimerCounter");
  }

  // Cria SoftwareTimer  TimerPulso
  TimerPulso = xTimerCreate("TIMER_PULSO",pdMS_TO_TICKS(SW_TIMER_PERIOD_PULSO),pdTRUE,NULL,Callback_TimerPulso);
  if(TimerPulso == NULL)
  {
    Serial.printf("\n\rFalha em criar SW_Timer TimerPulso");
  }

  // Cria SoftwareTimer  TimerBuzzer
  TimerBuzzer = xTimerCreate("TIMER_PULSO",pdMS_TO_TICKS(SW_TIMER_PERIOD_BUZZER),pdTRUE,NULL,Callback_TimerBuzzer);
  if(TimerBuzzer == NULL)
  {
    Serial.printf("\n\rFalha em criar SW_Timer TimerBuzzer");
  }

  // Cria Fila de mensagem QueuePulso
  QueuePulso = xQueueCreate( NUMBER_OF_ELEMENTS, sizeof(uint32_t) );
  if(QueuePulso == NULL)
  {
    Serial.printf("\n\rFalha em criar a fila QueuePulso");
  }

  // Cria tarefas da aplicação
  xTaskCreate(Tarefa_Pulso, "PULSO", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 2, NULL);
  xTaskCreate(Tarefa_Buzzer, "BUZZER", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY + 1, NULL);
}

void loop() 
{
  Serial.printf("\n\rSupende tarefa LOOP");
  vTaskSuspend(NULL);
}

/**
 * @brief 
 * @param parameters 
 */
void Tarefa_Pulso(void *parameters)
{
  static int valueOld = 0xFF;
  int value = 0;
  uint32_t valuePulso;
  
  while (1)
  {
    // le o valor do botão 
    value = digitalRead(PIN_BUTTON);

    // Detecta borda de descida
    if((value != valueOld) && (value == LOW))
    {
      // Zera contador do tempo do pulso
      PulseTimerCounter_Clear();
      // Inicia TimerPulso
      xTimerStart(TimerPulso, 0 );
      // Aciona LED 
      digitalWrite(LED_BOARD,HIGH);
      Serial.print(COLOR_GREEN);
      Serial.printf("\n\rDetecta borda de descida - Aciona LED on Board");
      Serial.print(COLOR_RESET);
    }
    else 
    {
      // Detecta borda de subida 
      if((value != valueOld) && (value == HIGH))
      {
        // Encera o TimerPulso
        xTimerStop(TimerPulso, 0);
        // Pega o tempo do pulso
        valuePulso = PulseTimerCounter_Get();
        if(valuePulso != 0) 
        {
          // Publica na Fila de MSG o tempo do Pulso 
          if(xQueueSend(QueuePulso, &valuePulso, pdMS_TO_TICKS(10) ) != pdTRUE)
          {
            Serial.print(COLOR_GREEN);
            Serial.printf("\n\rFalha em enviar os dados da fila");
            Serial.print(COLOR_RESET);
          }
          // Desliga o LED
          digitalWrite(LED_BOARD,LOW);
          Serial.print(COLOR_GREEN);
          Serial.printf("\n\rDetecta borda de subida - Desliga LED on Board");
          Serial.printf("\n\rLargura Pulso: %d", valuePulso);
          Serial.print(COLOR_RESET);
        }
      }
    }
    // update 
    valueOld = value;

    vTaskDelay(10/portTICK_PERIOD_MS);
  }
}

/**
 * @brief 
 * @param parameters 
 */
void Tarefa_Buzzer(void *parameters)
{
  static bool buzzerSts = false;
  uint32_t valuePulso;

  while (1)
  {
    // Checar se há conteudo na Fila
    if(xQueueReceive(QueuePulso, &valuePulso, pdMS_TO_TICKS(10)) == pdPASS)
    {
      buzzerSts = true;

      Serial.print(COLOR_YELLOW);
      Serial.printf("\n\rvaluePulso: %d",valuePulso);
      Serial.print(COLOR_RESET);
      // Checa se o pulso esta entre 1 a 2 segundo
      if((valuePulso >= 1000) && (valuePulso < 2000))
      {
        // Liga Buzzer por 1 segundo
        Buzzer.start();
        Buzzer.note(Buzzer_Note_C,1);
        // Inicia TimerBuzzer com 1 segundo
        xTimerChangePeriod(TimerBuzzer,pdMS_TO_TICKS(1000), 0);
        xTimerStart(TimerBuzzer, 0 );
        Serial.print(COLOR_YELLOW);
        Serial.printf("\n\rLiga Buzzer por 1 segundo c/ Nota C");
        Serial.print(COLOR_RESET);
      }
      else 
      {
        // Checa se o pulso esta entre 2 a 3 segundo
        if((valuePulso >= 2000) && (valuePulso < 3000))
        {
          // Liga Buzzer por 2 segundo
          Buzzer.start();
          Buzzer.note(Buzzer_Note_E,2);
          // Inicia TimerBuzzer com 2 segundo
          xTimerChangePeriod(TimerBuzzer,pdMS_TO_TICKS(2000), 0);
          xTimerStart(TimerBuzzer, 0 );
          Serial.print(COLOR_YELLOW);
          Serial.printf("\n\rLiga Buzzer por 2 segundo c/ Nota E");
          Serial.print(COLOR_RESET);
        }
        else 
        {
          if((valuePulso > 1) && (valuePulso < 1000))
          {
            // Liga Buzzer 
            Buzzer.start();
            Buzzer.note(Buzzer_Note_F,0);
            // Inicia TimerBuzzer com 500 millessegundos
            xTimerChangePeriod(TimerBuzzer,pdMS_TO_TICKS(valuePulso), 0);
            xTimerStart(TimerBuzzer, 0 );
            Serial.print(COLOR_YELLOW);
            Serial.printf("\n\rLiga Buzzer por %d milissegundos c/ Nota F",valuePulso);
            Serial.print(COLOR_RESET);
          }
          else
          {
            // Liga Buzzer por 500 millessegundos
            Buzzer.start();
            Buzzer.note(Buzzer_Note_A,4);
            // Inicia TimerBuzzer com 500 millessegundos
            xTimerChangePeriod(TimerBuzzer,pdMS_TO_TICKS(500), 0);
            xTimerStart(TimerBuzzer, 0 );
            Serial.print(COLOR_YELLOW);
            Serial.printf("\n\rLiga Buzzer por 0,5 segundo c/ Nota A");
            Serial.print(COLOR_RESET);
          }
        }
      }
    }

    // Checa se o semaforo binario esta live
    if(xSemaphoreTake(xSemaphore_BuzzerOff, pdMS_TO_TICKS(10)) == pdTRUE)
    {
      if(buzzerSts == true)
      {
        buzzerSts = false;
        // Encera o TimerBuzzer
        xTimerStop(TimerBuzzer, 0);
        // Desliga Buzzer
        Buzzer.stop();
        Serial.print(COLOR_YELLOW);
        Serial.printf("\n\rDesliga Buzzer");
        Serial.print(COLOR_RESET);
      }
    }
  }
}

/**
 * @brief 
 * @param timer 
 */
void Callback_TimerPulso(TimerHandle_t timer)
{
  if(timer == TimerPulso)
  {
    PulseTimerCounter_Increment();
  }
}

/**
 * @brief 
 * @param timer 
 */
void Callback_TimerBuzzer(TimerHandle_t timer)
{
  if(timer == TimerBuzzer)
  {
    xSemaphoreGiveFromISR(xSemaphore_BuzzerOff, (BaseType_t)(pdFALSE));
  }
}

/**
 * @brief 
 * @return uint32_t 
 */
uint32_t PulseTimerCounter_Get(void)
{
  uint32_t ret;
  // Obtem o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreTake(xMutex_PulseTimerCounter,portMAX_DELAY );
  ret = PulseTimerCounter;
  // libera o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreGive(xMutex_PulseTimerCounter);
  return ret;
}

/**
 * @brief 
 */
void PulseTimerCounter_Clear(void)
{
  // Obtem o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreTake(xMutex_PulseTimerCounter,portMAX_DELAY );
  PulseTimerCounter = 0;
  // libera o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreGive(xMutex_PulseTimerCounter);
}

/**
 * @brief  
 */
void PulseTimerCounter_Increment(void)
{
  // Obtem o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreTake(xMutex_PulseTimerCounter,portMAX_DELAY );
  PulseTimerCounter += SW_TIMER_PERIOD_PULSO;
  // libera o Mutex Variavel Global xMutex_PulseTimerCounter
  xSemaphoreGive(xMutex_PulseTimerCounter);
}

Resultado da Aplicação

A seguir, temos a imagem com as mensagem de cada tarefa impressas no terminal serial.

As mensagens na figura foram enumeradas para detalhar o comportamento do algoritmo implementado no projeto de demonstração:

  1. Após a inicialização da comunicação serial, é transmitida a mensagem “FreeRTOS – Mutex”.
  2. Observe que a mensagem “Suspende tarefa LOOP” é impressa uma única vez. Isso ocorre porque a função loop se comporta como uma tarefa agendada no escalonador do sistema operacional. Portanto, podemos utilizar os recursos oferecidos pelo FreeRTOS. Neste caso, foi utilizada a função que suspende a execução de tarefa (vTaskSuspend).
  3. A “Tarefa_Pulso” detecta as bordas de descida e subida do pulso, aciona o LED, realiza a medição da largura do pulso e imprime o valor da medida. Este primeiro pulso teve 310 milissegundos. 
  4. A “Tarefa_Buzzer” lê a informação da fila, imprime no barramento serial, realiza a verificação do tamanho da largura do pulso e aciona o buzzer por 310 milissegundos com a nota sonora “F”. Após expirar o tempo do “software timer”, o buzzer é desligado. 
  5. É detectado o segundo pulso, é executado todo o algoritmo da “Tarefa_Pulso”, que por sua vez mediu um pulso de 3070 milissegundos.    
  6. A “Tarefa_Buzzer” executa o seu algoritmo acionado o buzzer por 0,5 segundo com a nota sonora “A”.

Podemos observar que a cada novo pulso detectado, dependendo do tamanho da largura do pulso o buzzer é acionado com diferentes períodos de tempo e diferentes tons sonoros.  


Conclusão

Neste artigo, apresentamos mais um recurso bem interessante do FreeRTOS, o “Software Timer”. Esse recurso é muito útil, pois permite criar inúmeros temporizadores sem a necessidade de utilizar periféricos de hardware. No artigo, detalhamos as principais API’s para manipular o “Software Timer” no FreeRTOS. Também foi apresentada uma aplicação de demonstração. O projeto desenvolvido contava com dois temporizadores para exemplificar o seu uso. Ao final, analisamos o LOG de mensagem impressa no barramento serial para o melhor entendimento do algoritmo. Para os próximos artigos, serão apresentados mais recursos do FreeRTOS. O que você achou? Você já utiliza o FreeRTOS em seus projetos? Deixe o seu comentário abaixo.


Sobre o Autor


Evandro Teixeira

Desenvolvedor de sistemas embarcados com mais de 10 anos de experiência, com atuação em diferentes segmentos de mercado tais como eletromédicos, automação industrial, ITS (Sistemas Inteligentes de Transporte) e automotivo.


Eletrogate

30 de março de 2022

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!