FreeRTOS V7.4.0 e Queue Sets
- por Sergio Prado
Nesta última semana foi lançado o FreeRTOS versão 7.4.0. Dentre as alterações, temos uma nova API chamada Queue Sets. Vamos dar uma olhada nesta API e nas outras alterações desta nova versão do FreeRTOS?
QUEUE SETS
Um problema comum em aplicações com um RTOS é quando temos uma tarefa que pode receber múltiplos eventos de outras tarefas do sistema.
Se estes eventos tiverem características diferentes (uns apenas notificam, outros enviam um byte, outros enviam um buffer de dados, etc), você não pode usar apenas um queue ou semáforo para receber estes eventos.
Um design pattern comum usado nestes casos é a criação de uma estrutura para representar estes eventos contendo o ID do evento e um ponteiro para os dados do evento:
1 2 3 4 5 |
typedef struct { int id; void *data; } AppEvent; |
E dentro da tarefa, bloqueamos em um queue esperando pelo evento, tratando o evento recebido de acordo com seu ID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void vTaskEvent(void * pvParameters) { AppEvent event; for(;;) { /* wait event */ xQueueReceive(eventQueue, &event, portMAX_DELAY); /* handle event */ switch(event.id) { case EVENT_KEY: processEventKey(event.data); break; case EVENT_PRINTER: processEventPrinter(); break; case EVENT_DISPLAY: processEventDisplay(); break; [...] } } } |
Com esta nova versão do FreeRTOS, temos uma outra possível solução para este mesmo problema. É uma nova API do FreeRTOS chamada de Queue Sets.
Com Queue Sets é possível fazer com que uma tarefa possa bloquear (dormir) esperando por múltiplos semáforos e/ou queues ao mesmo tempo. O resultado é bem parecido com a chamada de sistema select() em sistemas Unix.
Para usar esta funcionalidade é necessário:
- Criar um Queue Set com a função xQueueCreateSet().
- Agrupar semáforos e/ou queues com a função xQueueAddToSet().
- Bloquear esperando pela recepção de um destes elementos com a função xQueueSelectFromSet().
No nosso exemplo, a implementação ficaria assim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
void vTaskEvent(void * pvParameters) { xQueueSetMemberHandle xActivatedMember; xQueueSetHandle xQueueSet; unsigned char key; /* create queue set */ xQueueSet = xQueueCreateSet(EVENT_MAX); /* add queues to queue set */ xQueueAddToSet(xQueueKey, xQueueSet); /* add semaphores to queue set */ xQueueAddToSet(xSemaphoreDisplay, xQueueSet); xQueueAddToSet(xSemaphorePrinter, xQueueSet); for( ;; ) { /* wait event */ xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); /* handle event */ if(xActivatedMember == xQueueKey) { xQueueReceive(xActivatedMember, &key, 0); processEventKey(key); } else if(xActivatedMember == xSemaphoreDisplay) { xSemaphoreTake(xActivatedMember, 0); processEventDisplay(); } else if(xActivatedMember == xSemaphorePrinter) { xSemaphoreTake(xActivatedMember, 0); processEventPrinter(); } [...] } } |
É sempre bom ter mais de uma opção para resolver um mesmo problema, mas eu ainda prefiro a primeira solução, pelos seguintes motivos:
- A implementação com Queue Sets usa mais memória RAM, já que precisamos criar um queue ou semáforo para cada tipo de evento.
- O código fica maior, ocupando mais espaço em flash.
- Consome mais ciclos de CPU, já que o fato do kernel precisar varrer um queue set leva mais tempo do que varrer um simples queue.
De qualquer forma, a principal motivação para o uso desta API é a migração de aplicações para o FreeRTOS. É muito comum os RTOSs de mercado terem uma função que bloqueia em múltiplos objetos do kernel. O uC/OS-III por exemplo tem a função OSPendMulti(). Então uma API deste tipo facilitaria a migração de aplicações que rodam em outros RTOSs para o FreeRTOS.
Mais informações sobre esta funcionalidade aqui.
LOW-POWER TICKLESS RTOS
É muito comum o uso do FreeRTOS em aplicações de baixo consumo de energia. E o FreeRTOS tem uma tarefa chamada idle que é executada quando nenhuma outra tarefa do sistema esta pronta para execução. Uma bela forma de colocar o sistema para dormir, certo? A aplicação não tem nenhuma tarefa pronta para executar, o kernel executa a tarefa idle, e em vez de ficarmos lá na idle desperdiçando preciosos ciclos de CPU, vamos colocar a CPU para dormir!
O problema é que o kernel usa uma interrupção periódica do sistema para controlar o tempo da aplicação. Esta interrupção, chamada de tick interrupt, fornece uma base de tempo para todo o sistema, incluindo rotinas de delay, bloqueio de tarefas e preempção. Então sempre que você colocar a CPU para dormir na tarefa idle, o kernel vai acordar a CPU no tick interrupt!
A boa notícia é que já existe uma solução para este problema desde a versão 7.3.0 do FreeRTOS. Ela é chamada de Low-power tickless RTOS. Com esta funcionalidade habilitada, o kernel desliga a interrupção do tick durante todo o tempo em que não existir nenhuma tarefa pronta para execução, mantendo a aplicação o máximo de tempo possível em modo de baixo consumo de energia.
Como esta funcionalidade é dependente de arquitetura, se você quiser usá-la, precisará de um porte para a sua plataforma. Mas no momento em que escrevo este artigo, se você estiver usando um ARM Cortex-M3, esta com sorte! Já existe um porte padrão desta funcionalidade para o Cortex M3! :)
Na versão 7.4.0 foram implementadas algumas melhorias nesta funcionalidade de Low-power Tickless RTOS. Para entender melhor esta implementação e aprender a usá-la, dê uma olhada na documentação aqui.
MAIS ALGUNS DETALHES
Ainda nesta versão tivemos algumas pequenas otimizações no porte para PIC32 e a adição de suporte à PIC24 com EDS (Extended Data Space). Foi corrigido um bug na funcionalidade de timers, além de mais algumas pequenas alterações e “perfumarias”. O histório completo de alterações pode ser acessado neste link aqui.
Segundo Richard Barry, criador do FreeRTOS, em 2012 houve um aumento de 30% de acessos únicos no site do projeto, e mais de 100.000 downloads dos fontes do FreeRTOS. São números que mostram o interesse da comunidade em um RTOS de código aberto e de qualidade como o FreeRTOS. Parabéns à equipe da Real Time Engineers!
Um abraço,
Sergio Prado
Sem Comentários
Nenhum comentário até agora... é a sua chance de ser o primeiro a comentar!