Principais conceitos de RTOS para iniciantes com Arduino e FreeRTOS

O Arduino é, sem sombra de dúvidas, uma das plataformas mais utilizadas para desenvolver projetos que envolvam sistemas embarcados, sobretudo no que diz respeito à prototipação rápida. Contando com uma comunidade grande, extremamente ativa e também com grande número de bibliotecas de suporte a hardware e comunicação (sensores, atuadores, protocolos de comunicação locais e em rede, etc.), o Arduino fica cada dia mais popular e adotada por aqueles que estão iniciando na área de sistemas embarcados.

 

Porém, o mundo não acaba nas facilidades que o Arduino oferece. Ao avançar os estudos, o aspirante a especialista em sistemas embarcados verá que há mundos novos em termos de conhecimento, sendo um deles o mundo dos Sistemas Operacionais de Tempo Real (ou, mais comumente encontrado na literatura, Real-Time Operating Systems - RTOS).

 

Dada a popularidade do Arduino, parece uma boa decisão utilizar a plataforma para ensinar novos conceitos e técnicas. E é justamente disso que este artigo irá tratar: a explicação dos principais conceitos de um RTOS para iniciantes, utilizando para exemplificar a plataforma o Arduino e como Sistema Operacional de Tempo Real o FreeRTOS.

 

O que é o FreeRTOS?

 

O FreeRTOS é um dos RTOS mais utilizados no mundo, devido tanto a questões de licença de software (ele pode ser aplicado em sistemas comerciais sem custos de licença) quanto ao grande número de microcontroladores e plataformas aos quais ele pode ser portado.

 

O FreeRTOS foi desenvolvido (e é constantemente melhorado) em parceria com as maiores fabricantes de chips, desenvolvimento esse que dura mais de 15 anos, seguindo normas rígidas de qualidade de software (ele segue quase todo o padrão MISRA). A popularidade é tanta que, em 2017, a Amazon adquiriu o FreeRTOS e lançou nele customizações interessantes para uso de seus serviços direcionados a IoT (leia mais disso aqui e aqui). Então sim, o FreeRTOS é "propriedade" da Amazon, e isso dá uma ideia da proporção desse projeto.

Figura 1 - FreeRTOS (logotipo)
Figura 1 - FreeRTOS (logotipo)

 

Caso desejar saber mais sobre o FreeRTOS, acesse o site oficial: www.freertos.org

 

Por que usar um sistema operacional?

 

Essa é uma pergunta que os mais iniciantes devem se fazer ao conhecer a possibilidade de se usar um sistema operacional. Afinal, se é possível fazer projetos sem sistema operacional, por que aprender a utilizá-los? A resposta contém vários argumentos, sendo os principais listados abaixo.

Para melhor compreendê-los, considere todas as funcionalidades que o projeto/produto possue como sendo as aplicações e toda a "base" como sendo o sistema operacional.

 

  1. Confiabilidade: acesso a hardware, gerenciamento de memória, comunicação entre processos distintos e muito mais já são providos pelo sistema operacional. Logo, como há um elemento cuidando (e muito bem) dessas coisas vitais a todo sistema embarcado, a confiabilidade no projeto / produto aumenta. Isso significa que bugs referentes a estes tópicos também tendem a diminuir (ou até mesmo desaparecer, a depender do quanto o sistema operacional foi testado e depurado), tornando a solução final mais confiável. 
  2. Portabilidade: portar uma aplicação de um projeto / produto para outro quando ambos utilizam o mesmo sistema operacional é, na maioria das vezes, algo simples. Isso ocorre pois toda a base do funcionamento (o sistema operacional em si) é comum. Isso leva a um ganho de tempo a prejuízo zero de confiabilidade.
  3. Reaproveitamento de código: aplicações podem ser instanciadas com parametrização diferentes (ou instanciadas com a mesma parametrização, como um novo processo). Isso significa reaproveitar código-fonte para realizar mais tarefas, o que é algo extremamente benéfico em ganho de tempo, confiabilidade (pois, se a aplicação já foi testada e depurada, provavelmente vai funcionar sendo instanciada várias vezes) e manutenção de código-fonte.
  4. Segurança: sistemas eletrônicos estão sempre sujeitos a ataques e busca por brechas para fraudes. Trabalhar com um sistema operacional significa que, uma vez que uma falha foi descoberta, a correção será feita a nível de sistema operacional, muitas vezes influenciando em nada no código-fonte de sua aplicação. Essa abordagem em camadas permite que sistemas embarcados sejam corrigidos de falhas graves sem alteração alguma nas aplicações.

 

Primeiro conceito: como arquitetar uma solução usando um RTOS

 

Como qualquer sistema operacional, os RTOS são capazes de executar diversas tarefas "simultaneamente".

 

Aqui cabe o primeiro grande conceito: o termo "simultaneamente" é usado devido a percepção de que tudo está executando ao mesmo tempo. Na realidade, o sistema operacional fica chaveando / alternando entre as tarefas, executando parte de cada uma delas por vez por alguns milissegundos e, posteriormente, passando a vez à próxima tarefa. Logo, um sistema operacional é, do ponto de vista de software, um executor muito rápido de tarefas, capaz de executar uma por vez mas de forma tão rápida que a percepção é a de que tudo está rodando em paralelo.

 

O core de um sistema operacional é chamado de Kernel. O Kernel é o responsável por gerenciar o hardware, executar as tarefas e memória da forma mais adequada possível, manter tudo rodando de forma estável e facilitar o desenvolvimento de projetos (uma vez que ele cuida do gerenciamento de hardware e memória).

 

Sendo assim, um RTOS (ou Kernel de RTOS), além de gerenciar hardware e memória, executa tarefas. Tais tarefas, em conjunto com o próprio Kernel, formam a parte de software de um projeto baseado em RTOS. Mas, como arquitetar soluções / software embarcado para um projeto ou produto neste conceito RTOS?

 

Em uma abordagem de software onde se usa um RTOS, todas as operações e funcionalidades de um projeto são definidas em tarefas (ou tasks, conforme mais comumente encontrado na literatura). Uma tarefa é como se fosse um programa / rotina isolada, modularizada o suficiente para ser uma tarefa isolada. Em suma uma tarefa pode ser resumida como uma funcionalidade do sistema embarcado.

 

Exemplo: imagine que você tenha que fazer o software embarcado de um rastreador simples (que obtém a posição via GPS, envia tal informação por rede e precisa piscar um LED indicando que está em operação, tudo utilizando um RTOS). Aqui, obter posições do GPS é uma tarefa / funcionalidade e o envio da mesma por rede e piscar o LED são outras duas tarefas distintas. Mesmo que, em algum momento, a tarefa de posicionamento tenha que se comunicar com a de envio por rede, são funcionalidades distintas e, portanto, são tarefas distintas.

 

Então, cada funcionalidade de um sistema embarcado pode ser encarado como uma tarefa ou task isolada, havendo ou não comunicação entre as tarefas. Pensar nesse tipo de divisão dos problemas em tarefas é o primeiro conceito de um RTOS.

 

Segundo conceito: uso de recursos compartilhados de forma segura

 

Uma vez que as funcionalidades de um projeto / produto são divididas em tarefas distintas (ou isoladas), uma situação exige cuidado: compartilhamento de recursos. Isso vem à tona quando se é pensado que pode haver a chance, remota ou não, de um recurso (como uma interface de comunicação ou um GPIO, por exemplo) ser requerido por uma tarefa enquanto outra tarefa já está fazendo uso do mesmo.

 

As consequências de não se proteger quanto a isso podem ser desastrosas. No caso de uma UART, pode-se ter um buffer de envio ou recepção corrompido (duas ou mais tasks alterando tal buffer), algo que pode não ser destruidor. Mas já imaginou se o recurso concorrido é um GPIO, onde esse GPIO controla algo forte e grande que, se manipulado incorretamente, pode causar danos (ou até mesmo morte) às pessoas? Pode ser trágico.

 

Para evitar que tarefas distintas tentem utilizar um recurso ao mesmo tempo, os RTOS possuem um recurso chamado semáforo. O semáforo não tem esse nome a toa. Ele se assemelha realmente ao semáforo de trânsito da vida real. No caso, as ruas / vias são recursos compartilhados por carros de um cruzamento, e se todos tentarem acessar a via ao mesmo tempo, acidentes acontecerão com certeza. Logo, o semáforo libera o tráfego em uma via de cada vez, de modo a evitar acidentes. Nessa analogia, as ruas são os recursos visados pelas tarefas, e as tarefas são os carros.

 

Portanto, um semáforo é uma ferramenta (normalmente provido pelo próprio sistema operacional) que sinaliza se determinado recurso está sendo utilizado naquele momento por alguma tarefa ou não. Dessa forma, a verificação do semáforo (e a ação ou não-ação dependendo de seu estado) é suficientemente seguro para evitar problemas ao se compartilhar recursos entre tarefas distintas. O fato de o semáforo ser um recurso provido pelo sistema operacional dá ainda mais segurança, uma vez que a chance de algo estar errado na implementação do semáforo é muito menor do que se fosse implementada do zero novamente pelo projetista (isso considerando que o RTOS em questão tenha sido suficientemente testado, claro).

 

Terceiro conceito: comunicação entre tarefas distintas

 

Até aqui, vimos que cada funcionalidade do RTOS deve ser isolada / encapsulada numa tarefa (task). Porém, e se tasks precisarem se comunicar?

 

Para isso, os RTOS oferecem mecanismos de comunicação inter-processos, sendo os mais comuns as filas (queues). Filas (queues) são capazes de trafegar dados entre tarefas, sempre numa via única (Task1 -> Task2, para envio de informação do tipo Task2 -> Task1 uma nova fila deve ser usada), com a garantia do RTOS que a informação está segura, íntegra (não corrompida) e disponível a qualquer tarefa que possuir o acesso (handler) da fila em questão.

 

Então lembre-se: em um RTOS, não utilize variáveis globais como "interface de comunicação" entre processos. Isso é estar totalmente sujeito a erros de escrita e leitura não íntegras e as filas resolvem esse problema definitivamente.

 

Paralelo dos conceitos com o FreeRTOS

 

De forma a exemplificar os conceitos de RTOS vistos neste artigo, seguem abaixo funções e métodos reais do FreeRTOS que atendem a tais conceitos.

 

Antes de tudo: prioridades de tarefas no FreeRTOS

 

As tarefas no FreeRTOS possuem prioridade. Isso significa que, num cenário onde há duas tarefas, A e B:

  • Se a prioridade da tarefa A for maior que prioridade da tarefa B: a execução da tarefa A será priorizada em relação à tarefa B;
  • Se a prioridade da tarefa A for menor que prioridade da tarefa B: a execução da tarefa B será priorizada em relação à tarefa A;
  • Se a prioridade da tarefa A for igual a prioridade da tarefa B: ambas as tarefas executarão em round-robin, ou seja, ambas vão "executar picado", por um tempo definido por vez (chamado quantum).

 

Conforme visto acima, a prioridade das tarefas é algo de importância crítica para um projeto que use o FreeRTOS. Isso é ainda mais importante quando se trata de tarefas que dependem de inputs (como dados numa fila) de outra tarefa. Nestes casos, a prioridade deve ser cuidadosamente pensada para que tarefas de prioridade maior não fiquem "eternamente" esperando outras de prioridade menor.
Portanto, definir inadequadamente as prioridades das tarefas a serem executadas no FreeRTOS oferece um risco enorme ao funcionamento do projeto como um todo. Tenha muita atenção a isso na hora de fazer seu projeto com FreeRTOS.

Criação de tarefas (tasks)

 

Para a criação de uma tarefa, o FreeRTOS disponibiliza a função xTaskCreate. Por "criação", entenda-se agendar a execução de uma função (desenvolvida pelo programador do projeto) no scheduler de tarefas do FreeRTOS. A criação de uma tarefa é feita no RTOS via função xTaskCreate, porém como esta contém peculiaridades em vários de seus parâmetros, vou descreve-los um a um.

Segue abaixo o protótipo da função e descrição de seus parâmetros.

  • Protótipo:
    BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
                                               const char * const pcName,
                                               configSTACK_DEPTH_TYPE usStackDepth,
                                               void *pvParameters,
                                               UBaseType_t uxPriority,
                                               TaskHandle_t *pxCreatedTask);

  •  Descrição dos parâmetros:
    - pvTaskCode: ponteiro para a função a ser executada nesta tarefa. Esta função é a implementação da tarefa em si, escrita pelo desenvolvedor / programador. É importante ressaltar que esta função deve ter, obrigatoriamente, tipo de retorno como void e parâmetro único do tipo ponteiro para void (motivo: dessa forma, pode-se passar quaisquer tipo de parâmetros para a tarefa, bastando a tarefa, quando em execução, fazer o casting para o tipo desejado).

    - pcName: nome (string) a ser associado a task. Isso é particularmente útil no debug de um projeto que utiliza o FreeRTOS.

    - usStackDepth: número de palavras (ou words) de memória a ser alocada como stack da tarefa. Não se esqueça: são words, não bytes de memória!

    - pvParameters: parâmetros (de quaisquer tipos) que devem ser passados à tarefa na sua inicialização.

    - uxPriority: prioridade da tarefa. Lembre-se que no FreeRTOS, ao contrário do comumente visto, quanto maior o número da prioridade, mais prioritária a tarefa é.

    - pxCreatedTask: handle para a tarefa. Este parâmetro é opcional, desde que você não precise suspender e reiniciar a tarefa durante o tempo de execução da tarefa.

Ainda, é possível criar mais tarefas (com handlers distintos) que utilizam a mesma função-base / implementação. Ou seja, um mesmo código pode ser reutilizado (instanciado) em duas ou mais tarefas. Isto eleva o nível de reaproveitamento de código e de prevenção de falhas, já que uma mesma função, já testada e validada, pode ser utilizada em tarefas distintas. Para isso ocorrer, quase sempre faz-se uso da passagem de parâmetros (através de pvParameters) e tratamento dos mesmos dentro da função, de modo que esta possa desempenhar papéis ligeiramente diferentes / correlatos com um mesmo código.

 

Para mais informações, acesse https://www.freertos.org/a00125.html

 

Criação de semáforos

 

O FreeRTOS oferece mais de um tipo de semáforo, porém neste artigo vamos nos concentrar no semáforo do tipo MUTEX. O termo MUTEX significa Mutual Exclusion (exclusão mútua), e um semáforo deste tipo tem como objetivo restringir o acesso a um recurso (uma interface de comunicação serial, por exemplo) a uma única tarefa, enquanto esta não "liberar" o semáforo. Logo, o ciclo de uso de semáforos MUTEX é conforme a seguir:

  1. Antes das tarefas iniciarem, o handler do semáforo (do tipo SemaphoreHandle_t) é criado e, em momento oportuno (antes da criação das tarefas, por exemplo), o semáforo é criado utilizando o seu handler e a função xSemaphoreCreateMutex
  2. Uma tarefa tenta obter o semáforo (função xSemaphoreTake)
  3. Se conseguir (ou seja, se nenhuma outra tarefa estiver utilizando o semáforo), a tarefa segue a execução normal de suas tarefas dependentes deste semáforo (como a manipulação de recursos compartilhados, como uma interface serial, por exemplo).
  4. Finalizado o uso dos recursos que dependiam do semáforo, o semáforo é liberado (função xSemaphoreGive).
    Dessa forma, o recurso protegido pelo semáforo pode ser utilizado por outra tarefa.

 

Pontos de atenção:

  1. De forma a serem visíveis por todas as tasks, é recomendável que os handlers dos semáforos sejam declarados globalmente.
  2. As funções xSemaphoreTake e xSemaphoreGive possuem um parâmetro muito importante chamado xTicksToWait. Ele especifica quantos ticks do processador deve-se aguardar na tentativa de se obter ou liberar um semáforo. Ainda, este tempo pode ser infinito (se atribuído a este parâmetro a macro portMAX_DELAY). 
    MUITA ATENÇÃO ao se usar a macro portMAX_DELAY. Se não bem pensado, este uso pode causar algo similar a um deadlock.

Para mais informações, consulte:

 

Criação de filas (queues)

 

Existe, a priori, um tipo de fila apenas no FreeRTOS, sendo que esta obedece ao tipo de leitura e escrita FIFO (First In First Out). Porém, existem várias formas de se escrever e ler desta fila, inclusive dependendo de onde no programa se está. 

Primeiramente, a fila precisa ser criada. Para a criação de uma fila, primeiro é criado um handler (do tipo QueueHandle_t) e, em momento oportuno (antes de criar as tarefas, por exemplo), cria-se a fila propriamente dita usando o seu handler e a função xQueueCreate. A criação de uma fila compreende informar quantos elementos deseja que ela tenha e de qual tipo de dados é cada elemento. 

 

De forma simplista, para inserir elementos numa fila há quatro opções de funções no FreeRTOS:

  1. xQueueSend: adiciona elemento a uma fila. Esta função NÃO deve ser utilizada dentro do tratamento de uma interrupção (ISR).
  2. xQueueSendFromISR: adiciona elemento a uma fila. Esta função DEVE SER SOMENTE USADA dentro do tratamento de uma interrupção (ISR).
  3. xQueueOverwrite: sobrescreve o primeiro elemento de uma fila. Essa função é especialmente útil quando se utiliza uma fila de um único elemento, onde somente o valor mais recente (última leitura de um sensor, por exemplo) é que importa ser mantido. Esta função NÃO deve ser utilizada dentro do tratamento de uma interrupção (ISR).
  4. xQueueOverwriteFromISR: sobrescreve o primeiro elemento de uma fila, devendo ser usada SOMENTE DENTRO DE UM TRATAMENTO DE INTERRUPÇÃO (ISR). Exatamente como o caso acima, essa função é especialmente útil quando se utiliza uma fila de um único elemento, onde somente o valor mais recente (última leitura de um sensor, por exemplo) é que importa ser mantido. 

 

Já para a leitura dos elementos de uma fila, também de forma simplista, há as seguintes opções no FreeRTOS:

  1. xQueueReceive: le um elemento da fila. Esta função NÃO deve ser utilizada dentro do tratamento de uma interrupção (ISR).
  2. xQueueReceiveFromISR: le um elemento da fila. Esta função SOMENTE DEVE SER UTILIZADA dentro do tratamento de uma interrupção (ISR).
  3. xQueuePeek: faz a leitura o elemento da fila, porém sem retirá-lo dela. Isso é útil quando a tarefa deseja verificar se a informação na fila deve ou não ser tratada por ela, sem alterar nada da fila para isso. Esta função NÃO deve ser utilizada dentro do tratamento de uma interrupção (ISR).
  4. xQueuePeekFromISR: exatamente conforme explicação acima, faz a leitura o elemento da fila, porém sem retirá-lo dela. Isso é útil quando a tarefa deseja verificar se a informação na fila deve ou não ser tratada por ela, sem alterar nada da fila para isso. Esta função SOMENTE DEVE SER UTILIZADA dentro do tratamento de uma interrupção (ISR).

 

Pontos de atenção:

  1. De forma a serem visíveis por todas as tasks, é recomendável que os handlers das filas sejam declarados globalmente.
  2. Todas as funções de inserção e leitura de elementos de uma fila possuem um parâmetro muito importante chamado xTicksToWait. Ele especifica quantos ticks do processador deve-se aguardar na tentativa de se ler ou interir elementos de uma fila. Ainda, este tempo pode ser infinito (se atribuído a este parâmetro a macro portMAX_DELAY). 
    MUITA ATENÇÃO ao se usar a macro portMAX_DELAY. Se não bem pensado, este uso pode causar algo similar a um deadlock.
  3. Se possível, sempre criar uma queue para cada tipo de informação / operação. Isso ajuda a modularização, organização de código, diminui chances de erros e melhora o debug. Uma queue para comunicação exclusiva entre duas tasks seria o cenário ideal do ponto de vista de modularização e legibilidade de código.

 

Para mais informações, consulte:

 

Ressalvas quanto ao port do FreeRTOS para o Arduino

 

O uso do FreeRTOS no Arduino abstrai a inicialização do scheduler de tarefas do FreeRTOS. Isso é feito de maneira automática, logo após a execução da função setup(). Em outras plataformas, isso pode não ser abstraído. Outra ressalva é que, no Arduino, as inicializações particulares do FreeRTOS referentes ao microcontrolador utilizado também são abstraídas, algo que é preciso ser feito em outras plataformas. 

Em suma: tenha em mente que o Arduino abstrai algumas coisas do FreeRTOS para o desenvolvedor, logo o código-fonte do uso do FreeRTOS em outra plataforma poderá ser ligeiramente diferente.

 

Projeto simples com FreeRTOS e Arduino

 

Agora que você conhece três dos principais conceitos de um RTOS, será feito um paralelo do uso dos mesmos com FreeRTOS no Arduino. Neste exemplo, utilizaremos tais conceitos aplicados no FreeRTOS para controlar um display LCD e um LED. O display exibe o valor da leitura da tensão no canal 0 do ADC (o qual possui um potenciômetro) e o LED acende ou apaga dependendo do valor desta leitura.

 

Material necessário

 

Para reproduzir os experimentos escritos aqui, você precisará de:

  • Um Arduino qualquer que utilize um microcontrolador Atmel com cabo de alimentação e programação.
    Nos meus experimentos utilizei um Arduino Nano V3, mas você pode utilizar tranquilamente um Arduino Mega ou Arduino Uno se assim desejar, desde que faça a adequação de pinos onde os periféricos serão ligados.
  • Um LED 
  • Um potenciômetro linear de 100 kOhm
  • Dois resistores de 330 Ohms / 0,25 W
  • Um display LCD 16x2 I²C
  • Um protoboard de 400 pontos

 

Biblioteca necessária

 

Antes de prosseguir, instale (utilizando o próprio gerenciador de bibliotecas da Arduino IDE) a biblioteca FreeRTOS by Richard Barry. Eu estou utilizando a versão 10.0.0-10. Se preferir, pode baixar a biblioteca do repositório Github oficial.

 

Circuito esquemático

 

O circuito esquemático é mostrado na figura 1.

 

Figura 2 - circuito esquemático
Figura 2 - circuito esquemático

 

Código-fonte completo

 

O código-fonte completo está abaixo. Leia com atenção os comentários nele contidos para maior compreensão do mesmo.

 

 

Considerações importantes

 

Se você for utilizar um Arduino que possui um microcontrolador com pouca memória Flash e/ou RAM (ATMEGA328, por exemplo) com FreeRTOS, evite trabalhar ou projetar sistemas com muitas tarefas. Recomenda-se isso pois pode ser que não haja memória suficiente.
Durante o desenvolvimento e testes, recomenda-se sempre avaliar o mínimo que a stack de cada tarefa atingiu (sugestão: utilize a função High Water Mark, disponibilizada pelo próprio FreeRTOS) e sempre fique de olho na memória RAM e Flash livres do microcontrolador. 

 

Conclusão

 

Neste artigo, foi possível aprender o porque de utilizar um sistema operacional num sistema embarcado, conceitos de um Sistema Operacional de Tempo Real (RTOS) e a prática destes conceitos e demais paradigmas de um RTOS utilizando FreeRTOS e Arduino.

Com a base fornecida neste artigo, é possível que o leitor expanda para seus projetos o uso de um RTOS, visando maior confiabilidade, portabilidade, reaproveitamento de código e segurança.

 

Referências

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Pedro Bertoleti
Sou engenheiro eletricista formado pela Faculdade de Engenharia de Guaratinguetá (FEG - UNESP) e trabalho com Android embarcado em Campinas-SP. Curioso e viciado em tecnologia, sempre busco me aprimorar na área de sistemas embarcados (modalidades bare-metal, RTOS, Linux embarcado e Android embarcado). Para mais informações, acesse minha página no Facebook:https://www.facebook.com/pbertoleti

25
Deixe um comentário

avatar
 
11 Comment threads
14 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
12 Comment authors
Pedro BertoletiJonathan GonzagaBertoletiCesar AugustoThiago Lucas Machado Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Bertoleti
Membro
Bertoleti

Boa Pedrão!!!!
Isso me fez abrir a mente pra começar usar o freeRTOS... rs

Uma dúvida que fiquei é sobre a questão das filas.
Qual a melhor maneira de passar várias variáveis entre uma task e outra?

Imagino que usando unitariamente, como no seu exemplo, não seja a melhor maneira.
Pensei em fazer uma struct dentro da task e passar essa struct com todas variáveis para uma fila, etc.

Acha uma boa ideia, ou existe alguma outra boa maneira de fazer isso?

Parabéns pelo belo artigo.
Abraço!

Bertoleti
Membro
Bertoleti

Ah sim.
Show de bola.... é o que farei então.

Obrigado.

Cesar Augusto
Membro
Cesar Augusto

Olá Pedro
Parabéns pelo artigo.
Que pontos você considera na hora de decidir se um projeto deve ou não usar um RTOS?

Thiago Lucas Machado
Membro
Thiago Lucas Machado

Bom dia.

Caramba, que artigo show de bola! Muito bom mesmo!

Ismael Lopes da Silva
Membro

Pedro, muito bom, gostei. Parabéns!

Pedro
Visitante
Pedro

Texto muito prolixo, cheio de blá blá blá desnecessário.

Fabio Luiz
Visitante
Fabio luiz

Show de artigo, obrigado por compartilhar seu conhecimento!

Fábio Souza
Visitante

Excelente Pedro!

Deixo como referência os livros do Max Back sobre o assunto: https://www.amazon.com.br/s?i=digital-text&rh=p_27%3AMax+Back&s=relevancerank&text=Max+Back&ref=dbs_p_ebk_r00_abau_000000

Aproveitando, você já fez a implementação nos AVRs sem ser pelo Arduino? Se sim, há vantagens?

Allan Uchoa Barbosa
Visitante
Allan Uchoa Barbosa

Interessantíssimo seu artigo!

André Feliciano
Visitante
André Feliciano

Muito bom o artigo. Fiz meu TCC aplicando o FreeRTOS no esp8266. Na época esse artigo iria ajudar muito.

Paulo Coutinho
Visitante
Paulo Coutinho

Caramba, um ótimo artigo e muito bem explicado. Parabéns ao autor. Tomara que venham outros, muito bom. Eu fiquei com algumas dúvidas em alguns pontos. Tem algum meio de contato via chat ou e-mail para facilitar? Abs!