Sobre o abuso do uso do sistema operacional em projetos para microcontroladores

Os microcontroladores modernos têm um desempenho bastante alto e isso dá a muitos programadores a oportunidade de pensar aproximadamente da seguinte maneira: - “Tudo bem se 1-5% do desempenho for para manutenção do sistema operacional. Mas meu código será facilmente depurado e explícito! ” Esses pensamentos são suportados por uma grande quantidade de memória não volátil (flash) para armazenar o código do sistema operacional e a memória operacional (RAM / SRAM) para alocar sua própria pilha para cada tarefa. No entanto, na maioria dos casos, essa idéia é errônea. E neste artigo eu vou lhe dizer o porquê.

Sobre os projetos com os quais trabalho


Na minha prática, muitas vezes tenho que trabalhar com um "designer". Descrevi essa abordagem em detalhes no meu artigo anterior sobre o uso de C ++ em microcontroladores . Então não contei a coisa mais importante. A maioria dos "blocos" deste "construtor" está de alguma forma ligada a um sistema operacional em tempo real. A maioria dos "blocos" possui fluxo próprio (tarefa, em termos do sistema operacional FreeRTOS em tempo real usado). E que, em média, o projeto tem cerca de 10 a 15 tarefas. Às vezes, esse valor atinge 35-40.

Onde tanto?


Aqui está uma pequena lista de tarefas encontradas em cada projeto:
  • Manutenção ADC (cada módulo é atendido por seu próprio fluxo);
  • manutenção wdt (se o sistema operacional travar, a tarefa não a redefinirá e o dispositivo será reiniciado);
  • trabalhar com páginas de configurações (um fluxo separado controla o trabalho com memória flash);
  • manutenção do protocolo de interação com o mundo exterior (a jusante da interface. Por exemplo, uart);

Já existem coisas específicas para cada dispositivo, como um fluxo para manutenção de termistores (recebendo dados do fluxo de medição ADC e convertendo esses dados em temperatura), pesquisando os periféricos externos e assim por diante.

Aparente simplicidade


Apesar do fato de haver muitas tarefas no projeto, cada uma delas está "oculta" dentro de um objeto da classe correspondente (lembre-se de que o construtor está em C ++, mas isso também pode ser imitado em C usando "programação em C em um estilo orientado a objetos". Mas é melhor não necessário ). Como os objetos desse “construtor” são globais e o FreeRTOS 9 é usado em projetos, o que suporta a criação de suas próprias entidades em buffers alocados pelo usuário, o uso da memória pode ser controlado no momento da vinculação. Portanto, do ponto de vista do monitoramento de vazamentos de memória - tudo é mais ou menos normal. Mas existem as seguintes nuances:
  • é preciso entender claramente quanto é necessária uma pilha para cada encadeamento. Nesse caso:
    • casos críticos devem ser levados em consideração (por exemplo, aninhamento com um determinado comportamento);
    • se funções de bibliotecas padrão forem usadas, também saiba como elas são organizadas ou, pelo menos, tenha uma idéia de quanto elas consumirão a pilha;

Além desse fato, parece que o uso do sistema operacional apenas melhorará a lógica do código e o tornará mais claro.

Abuso de funcionalidade do sistema operacional


Os principais problemas começam no momento em que você começa a esquecer o que está escrevendo especificamente para o microcontrolador. O SO impõe seus custos ao trabalhar com suas próprias entidades (como semáforos, mutexes, filas). Aqui está um exemplo de uma classe UART para implementar uma função de terminal . Na interrupção, um byte é recebido e, após passar o intervalo por caracteres de entrada válidos, é adicionado à fila com as substituições correspondentes (por exemplo, '\ n' muda para a sequência "\ n \ r"). Isso foi feito para proteger a porta para envio (já que a porta pode funcionar não apenas como um terminal. Os dados de log também podem ser enviados por ela). Por um lado, isso garante que a resposta seja enviada o mais rápido possível e não interfira no envio de dados de prioridade mais alta (além disso, enquanto os dados de prioridade mais alta são enviados, eles se acumulam no buffer, o que permite que o DMA seja usado para enviar a resposta). No entanto, a partir deste momento você entra em uma pista escorregadia. Em vez de escrever um monte na fila, pode-se configurar corretamente a interrupção em um buffer não vazio que não esteja atualmente trabalhando no UART e quando o DMA termina. Essa abordagem requer uma compreensão clara de como os periféricos funcionam. No entanto, reduz os custos a um mínimo absoluto, tornando a necessidade dessa solução zero.

Ignorando a funcionalidade de hardware do microcontrolador


Na minha prática, conheci um projeto com 18 temporizadores de software de sistema operacional sintonizados na mesma frequência. Ao mesmo tempo, havia cerca de 10 temporizadores no microcontrolador, dos quais apenas o systic foi usado. Cronometrar o planejador do sistema operacional. Essa decisão foi explicada pela falta de desejo de "mexer com o hardware" do microcontrolador. Ao mesmo tempo, cerca de 10 kb foram alocados à pilha para a função chamada pelo timer do software. De fato, cerca de 1 kb foi usado (curto). Isso ocorreu devido à "ambiguidade do que está acontecendo dentro das bibliotecas chamadas".
Nesse caso, era possível selecionar com segurança o TIM6 (no caso de usar stm32f4), o que geraria uma interrupção com uma determinada frequência e, dentro dele, simplesmente chamaria as funções necessárias.

Usando um loop infinito em vez de uma máquina de estado


Como uma coluna separada, destacaria a incapacidade de alguns programadores de escrever máquinas compactas de estados finitos e, em vez disso, criaria um fluxo no qual há um loop infinito que inicia seu trabalho obtendo algo da fila. Curiosamente, como criar máquinas compactas de estados finitos por meio da própria linguagem está escrito neste artigo .

Ignorando o "planejador de hardware"


Muitos microcontroladores de trinta e dois bits possuem um sofisticado controlador de interrupção com um sistema de prioridade personalizável. No caso do stm32f4, ele tem o nome NVIC e pode definir prioridades de interrupção com 16 níveis (sem considerar os subníveis).
A maioria dos aplicativos no FreeRTOS com os quais tive que lidar poderia ser escrita como máquinas de estado chamadas em interrupções com prioridades configuradas corretamente. E caso o processador retorne à "execução normal" - vá para "dormir". Nesse caso, não seria necessário bloquear o acesso à maioria dos recursos (variáveis ​​e outros). Os aplicativos perderiam um nível extra de abstração. E, neste caso - longe de ser gratuito. No entanto, essa abordagem requer um planejamento cuidadoso da arquitetura para cada projeto. Nos projetos, "designers" - todas as interrupções têm uma prioridade e, de fato, são necessárias para "filtrar" os dados. Em seguida, coloque as sobras na fila, de onde o fluxo de objetos da classe correspondente as coletará.

Sumário


Neste artigo, falei sobre os problemas básicos que você precisa enfrentar ao usar o sistema operacional em projetos para microcontroladores, e também examinei casos comuns de uso do sistema operacional quando isso poderia ter sido evitado sem perder a legibilidade e a lógica do código.

Source: https://habr.com/ru/post/pt461265/


All Articles