
Boa tarde Eu gostaria de falar sobre a arquitetura de aplicativos incorporados. Infelizmente, existem muito poucos livros sobre esse tópico e, devido ao fato de que, recentemente, o interesse pelo incorporado e pela IoT está crescendo, quero prestar atenção a esse problema. Neste artigo, gostaria de descrever uma das opções possíveis de como esses aplicativos podem ser projetados.
Esta é uma pergunta discutível! Portanto, eles se oferecem para compartilhar sua visão nos comentários!
Para começar, determinaremos a área: dentro da estrutura deste artigo, por desenvolvimento incorporado queremos dizer desenvolvimento de software para microcontroladores (daqui em diante MK, por exemplo, STM32) na linguagem C / Asm.
Projetos para sistemas baseados em MK podem ser condicionalmente divididos em
aqueles que não exigem e
exigem multitarefa. Quanto às soluções do primeiro tipo, elas geralmente não são muito complexas (do ponto de vista estrutural). Por exemplo, um projeto simples, no qual é necessário ler dados do sensor e exibi-los na tela, não requer multitarefa, basta implementar a execução seqüencial dessas operações.

Se o aplicativo for mais complexo: dentro da estrutura da qual é necessário ler dados dos sensores digitais e analógicos, salve os valores obtidos na memória (por exemplo, em um cartão SD), mantenha a interface do usuário (tela + teclado) e forneça acesso aos dados por meio de uma interface digital (por exemplo, RS-485 / Modbus ou Ethernet / TCP / IP) e reagir o mais rápido possível a determinados eventos no sistema (pressionando botões de emergência, etc.); nesse caso, será difícil fazer isso sem multitarefa. Há duas maneiras de resolver o problema da multitarefa: implementá-lo você mesmo ou usar algum tipo de sistema operacional (a seguir denominado sistema operacional). Hoje, um dos sistemas operacionais mais populares em tempo real para sistemas embarcados é o FreeRTOS.
Vamos tentar imaginar como deve ser a arquitetura de um aplicativo incorporado "complexo" que executa um número bastante grande de operações heterogêneas. Admito que é possível propor uma opção ainda mais complicada, que envolve a resolução de problemas de processamento de som, criptografia etc., mas continuaremos com a opção descrita acima.
Definimos a tarefa com mais clareza, mesmo que em nosso aplicativo seja necessário:
- Leia os dados dos sensores no RS-485 / Modbus.
- Leia os dados dos sensores no barramento I2C.
- Leia dados de entradas digitais.
- Saída do relé de controle.
- Manter a interface do usuário (tela + teclado).
- Forneça acesso aos dados via RS-485 / Modbus.
- Salve dados em mídia externa.
Porque precisamos implementar um número suficientemente grande de subtarefas diferentes; usaremos o sistema operacional em tempo real (por exemplo, o já mencionado FreeRTOS) como base. Às vezes, os threads no sistema operacional são chamados de tarefas - semelhantes ao FreeRTOS. Quero avisá-lo imediatamente: não haverá código-fonte no artigo, é o aspecto arquitetural desta questão que é do seu interesse.
Se analisarmos a tarefa, podemos ver que diferentes componentes do sistema usam os mesmos dados. Por exemplo: os dados dos sensores devem ser obtidos, exibidos em uma tela, gravados em um meio e fornecidos a sistemas externos para leitura. Isso sugere que é necessário algum tipo de banco de dados em tempo real (RTDB) para armazenar e fornecer os dados mais relevantes para vários subsistemas.
As tarefas executadas no sistema (leitura de dados, gravação, exibição etc.) podem ter requisitos diferentes para a frequência da chamada. Não faz sentido atualizar os dados no visor com uma frequência de 1 vez por 100 ms, porque isso não é crítico para uma pessoa, mas geralmente é necessário ler dados dos sensores (especialmente se for necessário executar ações de controle sobre eles) (embora isso possa não ser possível, dependendo da TK). Outro ponto importante está relacionado à solução do problema de acesso aos mesmos dados para leitura e escrita. Por exemplo: um fluxo que interroga os sensores grava os valores recebidos no RTDB e, nesse momento, o fluxo responsável pela atualização das informações no visor os lê. Aqui, os mecanismos de sincronização fornecidos pelo sistema operacional nos ajudarão.
Vamos começar a projetar a arquitetura do nosso aplicativo!
Banco de dados em tempo real

Uma estrutura comum que contém o conjunto necessário de campos ou uma matriz pode servir como base. Para acessar o "RTDB", usaremos a API, que nos permitirá escrever e ler dados do banco de dados. A sincronização de acesso a dados dentro das funções da API pode ser construída nos mutexes fornecidos pelo sistema operacional (ou usar algum outro mecanismo).

Trabalhando com sensores nos pneus
Trabalhar com sensores envolve o seguinte:
- leitura de dados;
- processamento de dados (se necessário), que inclui:
- verificação de validação;
- escala
- filtragem
- validação de valores válidos;
- gravação de dados recebidos em RTDB.
Todo esse trabalho pode ser feito em uma tarefa.

"Port" - o verdadeiro porto do MK;
“Driver de protocolo” - driver de protocolo (por exemplo, Modbus). Para esse driver, é aconselhável criar sua interface e trabalhar com ela. Dentro da estrutura dessa interface, é possível implementar o controle de acesso ao recurso através de mutexes, como foi feito para o "RTDB". Alguns desenvolvedores propõem fazer isso no nível da porta, para garantir que ninguém mais escreva nada nessa porta enquanto transmitimos nossos pacotes Modbus através dela.
“Leitor do sensor” - uma tarefa (tarefa) que pesquisa sensores, organiza as informações recebidas e as grava no “RTDB”.
"RTDB" é o banco de dados em tempo real descrito acima na seção correspondente.
A inscrição “Pr: 1” sobre a tarefa significa prioridade; o resultado final é que cada tarefa pode ter prioridade se duas tarefas aguardando pelo tempo do processador tiverem prioridades diferentes, o recurso receberá a que tiver a prioridade mais alta. Se as tarefas tiverem a mesma prioridade, será iniciada aquela com maior tempo de espera.
Trabalhar com entradas discretas
Em geral, o trabalho com entradas digitais pode ser organizado da mesma maneira que com sensores digitais. Mas pode ser necessário responder rapidamente às mudanças no status das entradas. Por exemplo, com o toque de um botão, feche a saída do relé o mais rápido possível. Nesse caso, é melhor usar a seguinte abordagem: para processar a saída do relé, criamos uma tarefa separada especial com uma prioridade mais alta que o restante. Dentro desta tarefa há um semáforo que está tentando capturar. Uma interrupção é acionada para acionar uma entrada digital específica, na qual o semáforo mencionado acima é redefinido. Porque como a prioridade de interrupção é máxima, a função associada a ele será executada quase instantaneamente; no nosso caso, redefinirá o semáforo e, depois disso, a próxima tarefa na fila de execução será aquela na qual o relé é controlado (porque possui a prioridade é maior que outras tarefas e o bloqueio de espera pelo semáforo é removido).
É assim que o esquema deste subsistema pode parecer.

Além da resposta rápida para alterar o estado de uma entrada específica, você também pode definir a tarefa "Leitor de DI" para ler o status de entradas discretas. Essa tarefa pode ser independente ou chamada pelo timer.
O trabalho do “Manipulador de interrupção” e “Controlador de relé” na forma de diagramas é apresentado abaixo.

Gravando dados em mídia externa
A gravação de dados em um meio externo é ideologicamente muito semelhante à leitura de dados de sensores digitais, apenas o movimento dos dados é realizado na direção oposta.

Lemos a partir de “RTDB” e escrevemos através do “Store driver” para um meio externo - pode ser um cartão SD, uma unidade flash USB ou qualquer outra coisa. Novamente, não esqueça de colocar wrappers mutex (ou quaisquer outras ferramentas para organizar o acesso ao recurso) nas funções da interface!
Fornecendo acesso a dados em tempo real
Um ponto importante é o fornecimento de dados do "RTDB" para sistemas externos. Pode ser quase todas as interfaces e protocolos. Diferentemente de vários subsistemas considerados, a principal diferença disso é que alguns dos protocolos amplamente utilizados em sistemas de automação possuem requisitos especiais para o tempo de resposta à solicitação, se a resposta não chegar dentro de um certo tempo, considera-se que não há comunicação, mesmo que ele (a resposta) venha depois de um tempo. E desde o acesso a “RTDB” em nosso exemplo pode ser temporariamente bloqueado (por mutex), é necessário fornecer proteção para o dispositivo mestre externo (mestre é um dispositivo que tenta ler dados nossos) contra esse bloqueio. Também vale a pena considerar a proteção do próprio dispositivo contra o fato de que o mestre o interrogará com alta frequência, inibindo assim a operação do sistema através da leitura constante de "RTDB". Uma solução é usar um buffer intermediário.

O "Atualizador de dados" lê os dados do "RTDB" em uma determinada frequência e adiciona o que lê no "Cache do protocolo", do qual o "Manipulador de protocolo" coleta os dados. Nesse caso, há um problema de bloqueio no nível do cache do protocolo. Para resolvê-lo, você pode criar outro cache no qual o "Manipulador de protocolo" armazenará dados caso não possa ler do "Cache do protocolo" bloqueado, além disso:
- tornar o "manipulador de protocolo" uma prioridade mais alta;
- aumente o período de leitura de "RTDB" para "Atualizador de dados" (que é uma solução mais ou menos).
Trabalhar com interface do usuário
Trabalhar com a interface do usuário envolve atualizar os dados na tela e trabalhar com o teclado. A arquitetura deste subsistema pode ser assim.

O trabalhador da interface do usuário é responsável pela leitura das teclas digitadas, obtendo dados do “RTDB” e atualizando a exibição que o usuário vê.
Estrutura geral do sistema
Agora dê uma olhada no que aconteceu no final.

Para equilibrar a carga, você pode definir caches adicionais, como fizemos no subsistema responsável por fornecer acesso a esses sistemas externos. Algumas tarefas de transferência de dados podem ser resolvidas usando filas, pois geralmente são suportadas por sistemas operacionais em tempo real (com certeza, no FreeRTOS).
Só isso, espero que tenha sido interessante.
PSComo literatura, eu recomendaria “Making Embedded Systems: Design Patterns for Great Software” Elecia White e artigos de
Andrey Kournits “FreeRTOS - um sistema operacional para microcontroladores”