- Como funciona o RTOS?
- Termos usados com frequência em RTOS
- Instalando a biblioteca Arduino FreeRTOS
- Diagrama de circuito
- Arduino FreeRTOS Exemplo - Criação de tarefas FreeRTOS no Arduino IDE
- Implementação de tarefas FreeRTOS no IDE Arduino
O SO presente nos dispositivos embarcados é denominado RTOS (Sistema Operacional em Tempo Real). Em dispositivos embarcados, as tarefas em tempo real são críticas, onde o tempo desempenha um papel muito importante. As tarefas em tempo real são Determinísticas do Tempo, o que significa que o tempo de resposta a qualquer evento é sempre constante, de modo que pode ser garantido que qualquer evento específico ocorrerá em um horário fixo. O RTOS é projetado para executar aplicativos com temporização muito precisa e um alto grau de confiabilidade. O RTOS também ajuda na multitarefa com um único núcleo.
Já cobrimos um tutorial sobre como usar RTOS em sistemas embarcados onde você pode saber mais sobre RTOS, a diferença entre OS e RTOS de uso geral, diferentes tipos de RTOS, etc.
Neste tutorial, começaremos com FreeRTOS. FreeRTOS é uma classe de RTOS para dispositivos embarcados que é pequena o suficiente para funcionar em microcontroladores de 8/16 bits, embora seu uso não se limite a esses microcontroladores. É um código totalmente aberto e seu código está disponível no github. Se conhecermos alguns conceitos básicos de RTOS, então é muito fácil usar o FreeRTOS porque ele tem APIs bem documentadas que podem ser usadas diretamente no código sem conhecer a parte de back-end da codificação. A documentação completa do FreeRTOS pode ser encontrada aqui.
Como o FreeRTOS pode ser executado em MCU de 8 bits, também pode ser executado na placa Arduino Uno. Temos que apenas baixar a biblioteca FreeRTOS e então começar a implementar o código usando APIs. Este tutorial é destinado a um iniciante completo. Abaixo estão os tópicos que iremos cobrir neste tutorial do Arduino FreeRTOS:
- Como funciona o RTOS
- Alguns termos usados com frequência em RTOS
- Instalando FreeRTOS no Arduino IDE
- Como criar tarefas FreeRTOS com exemplo
Como funciona o RTOS?
Antes de começar com o RTOS funcionando, vamos ver o que é uma Tarefa. Tarefa é um pedaço de código que pode ser executado na CPU. Então, se você quiser realizar alguma tarefa, deve ser agendada usando atraso de kernel ou usando interrupções. Este trabalho é feito pelo Scheduler presente no kernel. Em um processador de núcleo único, o agendador ajuda as tarefas a serem executadas em um intervalo de tempo específico, mas parece que diferentes tarefas estão sendo executadas simultaneamente. Cada tarefa é executada de acordo com a prioridade dada a ela.
Agora, vamos ver o que acontece no kernel RTOS se quisermos criar uma tarefa para LED piscando com um intervalo de um segundo e colocar essa tarefa na prioridade mais alta.
Além da tarefa de LED, haverá mais uma tarefa que é criada pelo kernel, ela é conhecida como tarefa ociosa. A tarefa ociosa é criada quando nenhuma tarefa está disponível para execução. Esta tarefa sempre é executada na prioridade mais baixa, ou seja, prioridade 0. Se analisarmos o gráfico de tempo dado acima, pode-se ver que a execução começa com uma tarefa de LED e é executada por um tempo especificado e, em seguida, para o tempo restante, a tarefa ociosa é executada até que ocorra uma interrupção de tick. Então o kernel decide qual tarefa deve ser executada de acordo com a prioridade da tarefa e o tempo total decorrido da tarefa de LED. Quando 1 segundo é completado, o kernel escolhe a tarefa conduzida novamente para executar porque ela tem uma prioridade maior do que a tarefa ociosa, podemos também dizer que a tarefa LED antecipa a tarefa ociosa. Se houver mais de duas tarefas com a mesma prioridade, elas serão executadas no modo round-robin por um tempo especificado.
Abaixo do diagrama de estado, uma vez que mostra a mudança da tarefa sem execução para o estado de execução.
Cada tarefa recém-criada vai para o estado Pronto (parte do estado de não execução). Se a tarefa criada (Tarefa1) tiver a prioridade mais alta do que outras tarefas, ela passará para o estado de execução. Se esta tarefa em execução for precedida pela outra tarefa, ela voltará ao estado de prontidão novamente. Caso contrário, se a tarefa1 for bloqueada usando a API de bloqueio, a CPU não se envolverá com essa tarefa até o tempo limite definido pelo usuário.
Se a Tarefa1 for suspensa no estado de execução usando APIs suspensas, a Tarefa1 irá para o estado Suspenso e não estará disponível para o planejador novamente. Se você retomar a Tarefa1 no estado suspenso, ela voltará ao estado de pronto, como você pode ver no diagrama de blocos.
Esta é a ideia básica de como as Tarefas são executadas e alteram seus estados. Neste tutorial, implementaremos duas tarefas no Arduino Uno usando a API FreeRTOS.
Termos usados com frequência em RTOS
1. Tarefa: É um trecho de código que pode ser executado na CPU.
2. Scheduler: é responsável por selecionar uma tarefa da lista de estado pronto para o estado de execução. Os agendadores são frequentemente implementados para manter todos os recursos do computador ocupados (como no balanceamento de carga).
3. Preempção: É o ato de interromper temporariamente uma tarefa já em execução com a intenção de retirá-la do estado de execução sem sua cooperação.
4. troca de contexto: Em preempção baseado em prioridade, o programador compara a prioridade de tarefas em execução com uma prioridade da lista de tarefas pronto em cada systick interrupção. Se houver alguma tarefa na lista cuja prioridade seja maior do que a tarefa em execução, ocorre a troca de contexto. Basicamente, neste processo, os conteúdos de diferentes tarefas são salvos em suas respectivas memórias de pilha.
5. Tipos de políticas de agendamento:
- Agendamento preventivo: Neste tipo de agendamento, as tarefas são executadas com fração de tempo igual, sem considerar as prioridades.
- Preemptivo baseado em prioridade: a tarefa de alta prioridade será executada primeiro.
- Agendamento cooperativo: A troca de contexto acontecerá apenas com a cooperação de tarefas em execução. A tarefa será executada continuamente até que o rendimento da tarefa seja chamado.
6. Objetos Kernel: Para sinalizar a tarefa para realizar algum trabalho, o processo de sincronização é usado. Para realizar este processo, são usados objetos Kernel. Alguns objetos do Kernel são Eventos, Semáforos, Filas, Mutex, Caixas de correio, etc. Veremos como usar esses objetos nos próximos tutoriais.
A partir da discussão acima, tivemos algumas idéias básicas sobre o conceito de RTOS e agora podemos implementar o projeto FreeRTOS no Arduino. Portanto, vamos começar instalando as bibliotecas FreeRTOS no Arduino IDE.
Instalando a biblioteca Arduino FreeRTOS
1. Abra o IDE Arduino e vá para Sketch -> Incluir Biblioteca -> Gerenciar Bibliotecas . Pesquise por FreeRTOS e instale a biblioteca conforme mostrado abaixo.
Você pode baixar a biblioteca do github e adicionar o arquivo.zip em Sketch-> Incluir biblioteca -> Adicionar arquivo .zip .
Agora, reinicie o IDE do Arduino. Esta biblioteca fornece alguns exemplos de código, também que podem ser encontrados em Arquivo -> Exemplos -> FreeRTOS conforme mostrado abaixo.
Aqui vamos escrever o código do zero para entender o funcionamento, depois você pode verificar os códigos de exemplo e usá-los.
Diagrama de circuito
Abaixo está o diagrama de circuito para a criação de tarefa de LED piscando usando FreeRTOS no Arduino:
Arduino FreeRTOS Exemplo - Criação de tarefas FreeRTOS no Arduino IDE
Vamos ver uma estrutura básica para escrever um projeto FreeRTOS.
1. Primeiro, inclua o arquivo de cabeçalho Arduino FreeRTOS como
#incluir
2. Dê o protótipo de função de todas as funções que você está escrevendo para execução, que é escrito como
void Task1 (void * pvParameters); void Task2 (void * pvParameters); .. ….
3. Agora, na função void setup () , crie tarefas e inicie o agendador de tarefas.
Para criar uma tarefa, a API xTaskCreate () é chamada na função de configuração com certos parâmetros / argumentos.
xTaskCreate (TaskFunction_t pvTaskCode, const char * const pcName, uint16_t usStackDepth, void * pvParameters, UBaseType_t uxPriority, TaskHandle_t * pxCreatedTask);
Existem 6 argumentos que devem ser passados ao criar qualquer tarefa. Vamos ver quais são esses argumentos
- pvTaskCode: É simplesmente um ponteiro para a função que implementa a tarefa (na verdade, apenas o nome da função).
- pcName: um nome descritivo para a tarefa. Isso não é usado pelo FreeRTOS. Ele é incluído apenas para fins de depuração.
- usStackDepth: Cada tarefa tem sua própria pilha exclusiva, que é alocada pelo kernel para a tarefa quando a tarefa é criada. O valor especifica o número de palavras que a pilha pode conter, não o número de bytes. Por exemplo, se a pilha tiver 32 bits de largura e usStackDepth for passado como 100, 400 bytes de espaço de pilha serão alocados (100 * 4 bytes) na RAM. Use isso com sabedoria porque o Arduino Uno tem apenas 2 Kbytes de RAM.
- pvParameters: parâmetro de entrada da tarefa (pode ser NULL).
- uxPriority: Prioridade da tarefa (0 é a prioridade mais baixa).
- pxCreatedTask: pode ser usado para passar um identificador para a tarefa que está sendo criada. Esse identificador pode então ser usado para fazer referência à tarefa em chamadas de API que, por exemplo, alteram a prioridade da tarefa ou excluem a tarefa (pode ser NULL).
Exemplo de criação de tarefa
xTaskCreate (tarefa1, "tarefa1", 128, NULL, 1, NULL); xTaskCreate (tarefa2, "tarefa2", 128, NULL, 2, NULL);
Aqui, a Tarefa2 tem prioridade mais alta e, portanto, é executada primeiro.
4. Após criar a tarefa, inicie o agendador em uma configuração vazia usando vTaskStartScheduler (); API.
5. A função Void loop () permanecerá vazia, pois não queremos executar nenhuma tarefa manualmente e infinitamente. Porque a execução da tarefa agora é controlada pelo Scheduler.
6. Agora, temos que implementar funções de tarefa e escrever a lógica que você deseja executar dentro dessas funções. O nome da função deve ser igual ao primeiro argumento da API xTaskCreate () .
void task1 (void * pvParameters) { while (1) { .. ..//sua lógica } }
7. A maior parte do código precisa da função de retardo para interromper a tarefa em execução, mas no RTOS não é sugerido usar a função Delay () , pois ela para a CPU e, portanto, o RTOS também para de funcionar. Portanto, o FreeRTOS tem uma API de kernel para bloquear a tarefa por um tempo específico.
vTaskDelay (const TickType_t xTicksToDelay);
Esta API pode ser usada para fins de atraso. Esta API atrasa uma tarefa por um determinado número de tiques. O tempo real durante o qual a tarefa permanece bloqueada depende da taxa de tique. A constante portTICK_PERIOD_MS pode ser usada para calcular em tempo real a partir da taxa de tick.
Isso significa que se você quiser um atraso de 200ms, basta escrever esta linha
vTaskDelay (200 / portTICK_PERIOD_MS);
Portanto, para este tutorial, usaremos essas APIs FreeRTOS para implementar três tarefas.
APIs a serem usadas:
- xTaskCreate ();
- vTaskStartScheduler ();
- vTaskDelay ();
Tarefa a ser criada para este tutorial:
- LED piscando no pino digital 8 com frequência de 200ms
- LED piscando no pino digital 7 com frequência de 300ms
- Impressão de números em monitor serial com frequência de 500ms.
Implementação de tarefas FreeRTOS no IDE Arduino
1. A partir da explicação da estrutura básica acima, inclua o arquivo de cabeçalho Arduino FreeRTOS. Em seguida, faça protótipos de funções. Como temos três tarefas, faça três funções e seus protótipos.
#include void TaskBlink1 (void * pvParameters); void TaskBlink2 (void * pvParameters); void Taskprint (void * pvParameters);
2. Na função void setup () , inicialize a comunicação serial a 9600 bits por segundo e crie todas as três tarefas usando a API xTaskCreate () . Inicialmente, defina as prioridades de todas as tarefas como '1' e inicie o planejador.
void setup () { Serial.begin (9600); xTaskCreate (TaskBlink1, "Task1", 128, NULL, 1, NULL); xTaskCreate (TaskBlink2, "Task2", 128, NULL, 1, NULL); xTaskCreate (Taskprint, "Task3", 128, NULL, 1, NULL); vTaskStartScheduler (); }
3. Agora, implemente todas as três funções conforme mostrado abaixo para piscar do LED da tarefa1.
void TaskBlink1 (void * pvParameters) { pinMode (8, OUTPUT); enquanto (1) { digitalWrite (8, ALTO); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (8, BAIXO); vTaskDelay (200 / portTICK_PERIOD_MS); } }
Da mesma forma, implemente a função TaskBlink2. A função Task3 será escrita como
void Taskprint (void * pvParameters) { int counter = 0; enquanto (1) { contador ++; Serial.println (contador); vTaskDelay (500 / portTICK_PERIOD_MS); } }
É isso aí. Concluímos com sucesso um projeto FreeRTOS Arduino para o Arduino Uno. Você pode encontrar o código completo junto com um vídeo no final deste tutorial.
Por fim, conecte dois LEDs nos pinos digitais 7 e 8, faça upload do código na placa Arduino e abra o monitor serial. Você verá que um contador está sendo executado uma vez em 500ms com o nome da tarefa conforme mostrado abaixo.
Além disso, observe os LEDs, eles estão piscando em intervalos de tempo diferentes. Tente brincar com o argumento de prioridade na função xTaskCreate . Altere o número e observe o comportamento no monitor serial e nos LEDs.
Agora, você pode entender os dois primeiros códigos de exemplo nos quais as tarefas de leitura analógica e digital são criadas. Desta forma, você pode fazer mais projetos avançados usando apenas as APIs do Arduino Uno e do FreeRTOS.