- Excluindo uma tarefa no FreeRTOS Arduino
- Qual é a fila no FreeRTOS?
- Criação de uma fila no FreeRTOS
- Diagrama de circuito
- Implementando o FreeRTOS Queue no Arduino IDE
No tutorial anterior, apresentamos o FreeRTOS no Arduino Uno e criamos uma tarefa para o LED piscando. Agora, neste tutorial, vamos mergulhar mais nos conceitos avançados de APIs RTOS e aprender sobre a comunicação entre diferentes tarefas. Aqui, também aprendemos sobre a Fila para transferir dados de uma tarefa para outra e demonstramos o funcionamento das APIs de fila ao fazer a interface do LCD 16x2 e do LDR com o Arduino Uno.
Antes de discutir sobre Filas, vamos ver mais uma API do FreeRTOS que é útil para excluir as tarefas quando terminar o trabalho atribuído. Às vezes, a tarefa precisa ser excluída para liberar a memória alocada. Na continuação do tutorial anterior, usaremos a função da API vTaskDelete () no mesmo código para excluir uma das tarefas. Uma tarefa pode usar a função de API vTaskDelete () para excluir a si mesma ou qualquer outra tarefa.
Para usar esta API, você deve configurar o arquivo FreeRTOSConfig.h . Este arquivo é usado para personalizar o FreeRTOS de acordo com a aplicação. É usado para alterar os algoritmos de agendamento e muitos outros parâmetros. O arquivo pode ser encontrado no diretório do Arduino, que geralmente está disponível na pasta Documentos do seu PC. No meu caso, ele está disponível em \ Documents \ Arduino \ libraries \ FreeRTOS \ src conforme mostrado abaixo.
Agora, abra este arquivo usando qualquer editor de texto e pesquise o #define INCLUDE_vTaskDelete e certifique-se de que seu valor seja '1' (1 significa habilitar e 0 significa desabilitar). É 1 por padrão, mas verifica isso.
Estaremos usando este arquivo de configuração com freqüência em nossos próximos tutoriais para definir os parâmetros.
Agora, vamos ver como excluir uma tarefa.
Excluindo uma tarefa no FreeRTOS Arduino
Para deletar uma tarefa, temos que usar a função API vTaskDelete (). Leva apenas um argumento.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: É o identificador da tarefa que deve ser excluída. É o mesmo que o 6º argumento da API xTaskCreate () . No tutorial anterior, esse argumento é definido como NULL, mas você pode passar o endereço do conteúdo da tarefa usando qualquer nome. Digamos que você queira definir o identificador da tarefa para a Tarefa 2, que é declarada como
TaskHandle_t any_name; Exemplo: TaskHandle_t xTask2Handle;
Agora, na API vTaskCreate () defina o 6º argumento como
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1, & xTask2Handle);
O conteúdo desta tarefa agora pode ser acessado usando o identificador fornecido por você.
Além disso, uma tarefa pode se excluir passando NULL no lugar de um identificador de tarefa válido.
Se quisermos excluir a Tarefa 3 da própria tarefa 3, você precisará escrever vTaskDelete (NULL); dentro da função Task3, mas se você quiser excluir a tarefa 3 da tarefa 2, escreva vTaskDelete (xTask3Handle); dentro da função task2.
No código do tutorial anterior, para excluir a Task2 da própria task2, basta adicionar vTaskDelete (NULL); na função void TaskBlink2 (void * pvParameters) . Então, a função acima será semelhante a esta
void TaskBlink2 (void * pvParameters) { Serial.println (“Tarefa2 em execução e prestes a ser excluída”); vTaskDelete (NULL); pinMode (7, OUTPUT); enquanto (1) { digitalWrite (7, HIGH); vTaskDelay (300 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (300 / portTICK_PERIOD_MS); } }
Agora, carregue o código e observe os LEDs e o monitor serial. Você verá que o segundo LED não está piscando agora e a tarefa2 foi excluída após encontrar a API de exclusão.
Portanto, esta API pode ser usada para interromper a execução de uma tarefa específica.
Agora, vamos começar com a fila.
Qual é a fila no FreeRTOS?
Fila é a estrutura de dados que pode conter o número finito de elementos de tamanho fixo e é operada no esquema FIFO (First-in First-out). As filas fornecem um mecanismo de comunicação tarefa a tarefa, tarefa a interrupção e interrupção a tarefa.
O número máximo de elementos que a fila pode conter é chamado de “comprimento”. Tanto o comprimento quanto o tamanho de cada elemento são definidos quando a fila é criada.
Um exemplo de como a fila é usada para transferência de dados é bem ilustrado na documentação do FreeRTOS, que pode ser encontrada aqui. Você pode entender facilmente o exemplo dado.
Depois de entender as Filas, vamos tentar entender o processo de criação de uma fila e tentar implementá-lo em nosso código FreeRTOS.
Criação de uma fila no FreeRTOS
Primeiro, descreva a declaração do problema que deve ser implementada com a ajuda da fila FreeRTOS e do Arduino Uno.
Queremos imprimir o valor do sensor LDR em 16 * 2 LCD. Portanto, existem duas tarefas agora
- Tarefa1 está obtendo valores analógicos de LDR.
- A tarefa2 está imprimindo o valor analógico no LCD.
Portanto, aqui a fila desempenha seu papel, pois enviar os dados gerados pela tarefa1 para a tarefa2. Na tarefa1, enviaremos o valor analógico para a fila e na tarefa2, o receberemos da fila.
Existem três funções para trabalhar com filas
- Criação de uma fila
- Enviando dados para a fila
- Recebendo dados da fila
Para criar a fila, use a API da função xQueueCreate (). São necessários dois argumentos.
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength: O número máximo de itens que a fila que está sendo criada pode conter a qualquer momento.
uxItemSize: O tamanho em bytes de cada item de dados que pode ser armazenado na fila.
Se esta função retornar NULL, a fila não será criada devido à memória insuficiente e se retornar um valor não NULL, a fila será criada com sucesso. Armazene este valor de retorno em uma variável para usá-lo como um identificador para acessar a fila, conforme mostrado abaixo.
QueueHandle_t queue1; queue1 = xQueueCreate (4, sizeof (int));
Isso criará uma fila de 4 elementos na memória heap de tamanho interno (2 bytes de cada bloco) e armazenará o valor de retorno na variável de manipulação da fila1 .
2. Envio de dados para a fila no FreeRTOS
Para enviar os valores para a fila, o FreeRTOS possui 2 variantes de API para esse fim.
- xQueueSendToBack (): Usado para enviar dados para o final (cauda) de uma fila.
- xQueueSendToFront (): Usado para enviar dados para a frente (cabeça) de uma fila.
Agora , xQueueSend () é equivalente e exatamente igual a xQueueSendToBack ().
Todas essas APIs levam 3 argumentos.
xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
xQueue: o identificador da fila para a qual os dados estão sendo enviados (gravados). Esta variável é a mesma usada para armazenar o valor de retorno da API xQueueCreate.
pvItemToQueue: um ponteiro para os dados a serem copiados na fila.
xTicksToWait: O tempo máximo que a tarefa deve permanecer no estado Bloqueado para aguardar que o espaço fique disponível na fila.
Definir xTicksToWait como portMAX_DELAY fará com que a tarefa espere indefinidamente (sem tempo limite), desde que INCLUDE_vTaskSuspend seja definido como 1 em FreeRTOSConfig.h caso contrário, você pode usar a macro pdMS_TO_TICKS () para converter um tempo especificado em milissegundos em um tempo especificado em tiques.
3. Recebendo dados da fila no FreeRTOS
Para receber (ler) um item de uma fila, xQueueReceive () é usado. O item recebido é removido da fila.
Esta API também leva três argumentos.
xQueueReceive (QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
O primeiro e o terceiro argumentos são iguais ao envio da API. Apenas o segundo argumento é diferente.
const pvBuffer: Um ponteiro para a memória na qual os dados recebidos serão copiados.
Espero que você tenha entendido as três APIs. Agora, implementaremos essas APIs no IDE do Arduino e tentaremos resolver a declaração do problema que descrevemos acima.
Diagrama de circuito
É assim que fica na placa de ensaio:
Implementando o FreeRTOS Queue no Arduino IDE
Vamos começar a escrever código para nosso aplicativo.
1. Primeiro, abra o IDE do Arduino e inclua o arquivo de cabeçalho Arduino_FreeRTOS.h . Agora, se qualquer objeto de kernel, como fila, for usado, inclua o arquivo de cabeçalho dele. Como estamos usando LCD 16 * 2, inclua também a biblioteca para ele.
#include #include
2. Inicialize um identificador de fila para armazenar o conteúdo da fila. Além disso, inicialize os números dos pinos do LCD.
QueueHandle_t queue_1; LiquidCrystal lcd (7, 8, 9, 10, 11, 12);
3. Em void setup (), inicialize o LCD e o monitor serial com uma taxa de 9600 bauds. Crie uma fila e duas tarefas usando as respectivas APIs. Aqui, criaremos uma fila de tamanho 4 com tipo inteiro. Crie uma tarefa com prioridades iguais e depois tente brincar com este número. Finalmente, inicie o planejador conforme mostrado abaixo.
void setup () { Serial.begin (9600); lcd.begin (16, 2); queue_1 = xQueueCreate (4, sizeof (int)); if (queue_1 == NULL) { Serial.println ("Fila não pode ser criada"); } xTaskCreate (TaskDisplay, "Display_task", 128, NULL, 1, NULL); xTaskCreate (TaskLDR, "LDR_task", 128, NULL, 1, NULL); vTaskStartScheduler (); }
4. Agora, crie duas funções TaskDisplay e TaskLDR . Na função TaskLDR , leia o pino analógico A0 em uma variável, pois temos o LDR conectado ao pino A0 do Arduino UNO. Agora envie o valor armazenado na variável passando-o na API xQueueSend e envie a tarefa para o estado do bloco após 1 segundo usando a API vTaskDelay () conforme mostrado abaixo.
void TaskLDR (void * pvParameters) { int current_intensity; while (1) { Serial.println ("Tarefa1"); intensidade_atual = analogRead (A0); Serial.println (intensidade_atual); xQueueSend (queue_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / portTICK_PERIOD_MS); } }
5. Da mesma forma, crie uma função para TaskDisplay e receba os valores em uma variável que é passada para a função xQueueReceive . Além disso, xQueueReceive () retorna pdPASS se os dados podem ser recebidos com sucesso da fila e retorna errQUEUE_EMPTY se a fila está vazia.
Agora, exiba os valores no LCD usando a função lcd.print () .
void TaskDisplay (void * pvParameters) { int intensidade = 0; while (1) { Serial.println ("Tarefa2"); if (xQueueReceive (queue_1, & intensidade, portMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("Intensidade:"); lcd.setCursor (11, 0); lcd.print (intensidade); } } }
É isso aí. Concluímos a parte de codificação da implementação da fila. O código completo com um vídeo funcional pode ser encontrado no final.
Agora, conecte o LCD e o LDR com o Arduino UNO de acordo com o diagrama de circuito, carregue o código. Abra o monitor serial e observe as tarefas. Você verá que as tarefas estão mudando e os valores de LDR estão mudando de acordo com a intensidade da luz.
NOTA: A maioria das bibliotecas feitas para sensores diferentes não são suportadas pelo kernel do FreeRTOS devido à implementação da função de atraso dentro das bibliotecas. O atraso faz a CPU parar completamente, portanto, o kernel do FreeRTOS também para de funcionar e o código não executa mais e começa a se comportar mal. Portanto, temos que tornar as bibliotecas livres de atrasos para funcionar com o FreeRTOS.