Site icon Embarcados – Sua fonte de informações sobre Sistemas Embarcados

Arquitetura de desenvolvimento de software – parte II

arquitetura desenvolvimento de software

Na parte I comentamos sobre o processo de desenvolvimento mais simples: one-single-loop. Este modelo é bastante rápido e praticamente não insere sobrecarga de processamento ou memória. No entanto, o maior problema com esta arquitetura é a criação de rotinas que envolvam leituras de informação ou execuções periódicas. Em geral as leituras envolvem algum tipo de atraso, como a conversão de um sinal analógico ou a recepção de uma mensagem. O problema com as execuções periódicas, é que nem sempre o loop tem tempo constante, podendo variar seu período a cada execução. Mesmo quando o loop apresenta uma constância, o cálculo deste tempo não é simples de fazer, além de se alterar com cada nova adição ou remoção de código e funcionalidade.

Entre as soluções disponíveis para resolver estes problemas, uma se destaca: as interrupções. Elas permitem que uma função pré-definida seja executada sempre que um evento acontecer. Assim, é possível programar um sistema de modo que ele responda aos eventos e não apenas execute operações de modo sequencial. No entanto, é necessário que o hardware tenha suporte às interrupções. Entre as interrupções mais comuns implementadas pelos fabricantes temos:

Além das interrupções geradas pelos periféricos internos, alguns fabricantes oferecem a possibilidade de gerar uma interrupção por um evento externo, disponibilizando um terminal especificamente para isso.

No desenvolvimento orientado à interrupções, as funcionalidades do sistema são codificadas em funções que são executadas como resposta aos eventos. Esta abordagem reduz drasticamente a latência na resposta aos eventos. No entanto, alguns cuidados devem ser tomados.

A interrupção pausa o fluxo do programa principal, executa a função pré-definida e retorna. Com exceção do tempo transcorrido, o programa principal não percebe a pausa. Se a interrupção for algo recorrente, como uma função de controle temporizada, o tempo gasto na interrupção pode fazer com que o loop principal seja executado muito lentamente, ou até mesmo não seja executado.

De modo geral, é importante que as funções que serão executadas nas interrupções sejam breves. Sempre que possível, deixe o processamento pesado no programa principal.

Vamos para um exemplo prático: um interpretador de comandos via comunicação serial. Utilizando a arquitetura one-single-loop podemos projetar o sistema do seguinte modo:

O problema com essa abordagem é que as funções VerificaCRC() e a ExecutaAção() podem consumir tempo demais, fazendo com que alguns dos dados recebidos pela serial sejam perdidos. Utilizando a interrupção de chegada de byte na serial evita-se esse problema.

O problema de perder algum dado da serial é resolvido, no entanto outro problema é inserido. Como as duas funções, a principal e a interrupção, operam com o buffer, é possível que o buffer seja alterado na interupção enquanto a função principal calcula o CRC da mensagem. Isto pode levar à uma conclusão errada da função VerificaCRC() ou pior, a função ExecutaAcao() irá executar uma tarefa de maneira errada.

Existem algumas soluções para evitar esse problema: mutexes, semáforos, ou filas de mensagens para passar o evento para o programa principal. A solução mais simples é a utilização de uma flag indicando que a mensagem está em processamento e portanto a interrupção deve evitar alterar a mensagem.

Outra grande vantagem do uso da interrupção é a possibilidade de se utilizar o modo de baixo consumo de energia de modo simples. Como todas as ações no exemplo citado são executadas apenas quando há uma interrupção o sistema pode pausar o processamento enquanto não houver uma mensagem disponível no buffer. Sendo o sistema inteiro orientado à eventos que geram interrupção, ele automaticamente sairá do modo de baixo consumo sempre que algum evento importante acontecer.

O desenvolvimento do sistema pode ser, então, simplificado em 3 etapas:

  1. Atender as interrupções;
  2. Processar o resultado das interrupções no loop principal;
  3. Se não houver nada a ser processado entrar em modo de baixo consumo de energia.

Com a grande quantidade de microcontroladores voltados para baixo consumo existentes e sendo lançados (inclusive ARMs em DIP!) esta pode ser uma abordagem simples e eficiente.

Outros artigos da série

<< Arquitetura de desenvolvimento de software – parte IArquitetura de desenvolvimento de software – parte III >>