Sistemas embarcados contam com diversos timers físicos (hardware) para uso interno, como em PWM, ou de uso geral com extrema precisão e imunidade a interferências do código. Porém, em muitos projetos, podemos precisar de muito mais timers do que o embarcado nos disponibiliza e aí entra o software timer. Uma das únicas limitações para a quantidade de software timers no seu embarcado é a RAM disponível e, no ESP32 com ~300 KB de RAM livre, podemos criar até ~5800 timers independentes se for necessário.
Quais as vantagens e desvantagens do software timer no FreeRTOS?
Os software timers funcionam da mesma forma que os hardware timers, entretanto, há alguns detalhes que vale ressaltar:
- Assim como o hardware timer, o software timer não utiliza nenhum processamento da CPU enquanto ativo;
- Pode ser comparado a uma tarefa do RTOS;
- Todos os comandos do timer são enviados à tarefa “Timer Service ou Daemon task” por uma queue, o que vamos entender mais à frente.
- One-shot e Auto-reload.
Vantagens
- Não precisa de suporte do hardware, sendo de total responsabilidade do FreeRTOS;
- Limite de timers é a memória disponível;
- Fácil implementação.
Desvantagens
- Latência varia de acordo com a prioridade da tarefa “Timer Service” e frequência do FreeRTOS, ambos configuráveis;
- Pode perder comandos se usado excessivamente durante um curto período de tempo, já que a comunicação com a tarefa “Timer Service” é feita por uma queue, a qual pode ficar lotada.
Timer Service ou Daemon task
Essa tarefa é iniciada automaticamente junto ao scheduler caso você habilite os software timer no FreeRTOS. Ela é responsável por receber e executar os comandos sobre timers e também executar a função de callback quando o timer expira. Essa tarefa funciona exatamente igual a qualquer outra tarefa do RTOS, então, se houver dúvidas, basta se aprofundar nos detalhes sobre tarefas do RTOS. Como foi dito anteriormente, a prioridade é configurável e altamente aconselhável deixá-la maior que todas suas outras tarefas.
Veja nas figuras 1 e 2 a comunicação com a tarefa “Timer Service” através da queue.
Ao usar excessivamente comandos de controle do timer, a queue responsável por essa comunicação pode ficar lotada e, caso você deixe o parâmetro de espera das funções em zero, o comando será perdido. Por esse motivo, é sempre aconselhado a colocar um tempo de espera para que as funções aguardem a queue esvaziar. O motivo mais comum para lotar a queue da tarefa “Timer Service” é utilizar os comandos dentro de ISRs, já que uma ISR tem prioridade mais alta e não deixará tempo para a “Timer Service” processar dados anteriores caso venha acontecer alguma oscilação ou bouncing, por exemplo.
Vamos botar a mão na massa e criar alguns timers para aprender a usá-los e também verificar a frequência para saber se é estável ou não.
Código do projeto
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <freertos/timers.h>
#include <esp_system.h>
#include <esp_log.h>
SemaphoreHandle_t smf;//Cria o objeto do semaforo
TimerHandle_t tmr;//Cria o objeto do timer
void ISR(void*z)
{
int aux = 0;
xSemaphoreGiveFromISR(smf, &aux);//Libera o semaforo para uso
if (aux)
{
portYIELD_FROM_ISR();//Forca a troca de contexto se necessario
}
}
extern "C" void app_main()
{
smf = xSemaphoreCreateBinary();//Cria o semaforo
tmr = xTimerCreate("tmr_smf", pdMS_TO_TICKS(1000), true, 0, ISR);//Cria o timer com 1000ms de frequencia com auto-reaload
xTimerStart(tmr, pdMS_TO_TICKS(100));//Inicia o timer
while (1)
{
if (xSemaphoreTake(smf, portMAX_DELAY))
{
ESP_LOGI("Timer", "expirou!");//Ao obter o semaforo, enviara a string para o terminal
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
Testando esse simples código, já podemos visualizar no terminal que o microcontrolador está enviando as mensagens perfeitamente em 1 segundo, mas vamos analisar mais a fundo e ver se ele se mantém estável mesmo com frequências mais altas, lembrando que a frequência máxima do software timer está limitada à frequência do RTOS. Vamos observar o desempenho do timer com o analisador lógico nas figuras 3 e 4. Em ambos testes, a ISR do timer efetuou um pulso de 50 us em um GPIO.
Veja na figura 3 (clique para ampliar se necessário), o software timer efetuando o pulso de 50 us no GPIO com frequência de 10 Hz (100 ms), observe no canto direito da imagem os detalhes da análise de ~100 pulsos.
De acordo com o analisador lógico, nossa frequência média foi de 9,997 Hz (100,030009002701 ms), o que é aceitável para a maioria dos propósitos e deve-se lembrar do próprio erro do analisador lógico.
Agora vamos observar na figura 4, o software timer efetuando o pulso de 50 us no GPIO com frequência de 500 Hz (2 ms), observe no canto direito da imagem os detalhes da análise de ~100 pulsos.
De acordo com o analisador lógico, nossa frequência média foi de 499,8 Hz (2,000800320128 ms), o que também é aceitável para a maioria dos projetos e não devemos esquecer novamente sobre o erro do analisador lógico.
O software timer do FreeRTOS se mostra bem estável e pode ser extremamente útil na maioria dos projetos que usam timers, já que podemos trocar os timers físicos (hardware) pelo software caso a frequência não seja tão alta. Devemos lembrar que a frequência máxima indicada para uso do FreeRTOS é de 1000 Hz, deixando o software timer atrelado a esta frequência máxima também.
Saiba mais
Biblioteca rápida de Timer, Delay e Timeout sem desperdícios
Sistemas Operacionais de Tempo Real – Timers
Referências













Como assim “não consome nenhum processamento da CPU enquanto ativo”?
A CPU não precisa efetuar nenhum tipo de processamento para manter o “timer contando”, podemos dizer que ele “conta independentemente da CPU”.
José, na verdade ele demanda processamento da CPU sim, visto que o soft timer depende do tick timer, invocado a cada tick do kernel, alem disso o determinismo do timer vai variar a medida que timers entram e saem da timer list variando o uso de cpu para processamento do modulo de soft timers.
Sim sim, fiz apenas um resuminho (1* aproximação) que se usuário precisar, deve buscar nas referências para maiores detalhes. Mas realmente deve-se lembrar que é usado e obrigado por manter isso atualizado nos comentários.
Se o usuário ainda quiser ver uma parcela do uso, pode utilizar as funções de DEBUG para ver o tempo de absoluto da tarefa como vTaskGetRunTimeStats().