electronplace.com

...Just Because I Can Do It Now.

state-machine

Sistemas de controle que gerenciam sistemas elétricos ou mecânicos normalmente devem estar aptos a gerar ou responder para eventos sequenciais neste sistema. Esta habilidade em utilizar o tempo como uma parte da equação nessa pilotagem é uma das importantes habilidades de um microcontrolador que o torna adequado no controle de sistemas elétricos e mecânicos.

Todavia, a implementação de múltiplas sequências pode tornar-se longa e complexa se um estilo de codificação linear for usado. Uma construção simples, denominada de state machine, simplifica a tarefa de gerar uma sequência pelo desdobrar desta numa série de passos e então executá-los sequencialmente. Enquanto isto se parece com uma definição arbitrária de uma peça de código linear, a diferença é que aquelas seções individuais, ou passos em sequência, são codificados dentro de uma declaração SWITCH/CASE.

Isso quebra a sequência em unidades lógicas as quais podem ser facilmente reconhecidas na listagem do software e, mais importante, isto permite que outras funções possam ser realizadas entre esses passos individuais.  Isso é feito pela simples execução de um passo cada vez que é chamado. Chamando repetidamente a state machine resulta na execução de cada passo na sequência desejada. Para manter as sequências da state machine no lugar, uma variável de armazenamento é definida para determinar qual o passo da sequência será executado posteriormente. Esta variável é referenciada como a state variable, e é utilizada na declaração SWITCH/CASE para determinar qual o passo, ou o estado, na state machine está para ser executado quando a state machine é chamada.

Para esse sistema funcionar, a state variable deve ser incrementada ao final de cada estado. Todavia, também é verdade que a sequência dos estados podem precisar sofrer mudanças em razão das condições do sistema. Dado que a state variable determina qual estado é executado, isto segue que para modificar a sequência dos estados, um deles deve simplesmente carregar a state variable com o novo valor correspondente à nova direção que a sequência deve ir. Como poderá observar esta simples construção é muito poderosa, e é de fato a base para sistemas multitasking.

Assim, uma curta definição para uma state machine é como sendo uma coleção de passos (estados/states) selecionados para execução baseada no valor de uma state variable. Além disso, a manipulação do valor na state variable permite à state machine emular todas as declarações condicionais de controle de fluxo presentes na programação. Uma das vantagens de designs baseados em state machine é que isto permite a simples geração de uma sequência de eventos. Outra vantagem é a sua habilidade de reconhecer uma sequência de eventos.

Ela realize isso se utilizando da mudança condicional da state variable, tal como descrito no parágrafo anterior. A única diferença é que a state variable normalmente nunca modifica o seu próprio valor, a menos que um evento específico seja detectado. Como analogia, considere uma combinação de fechadura de teclado: para abrir a fechadura, os números devem ser digitados numa sequência específica como 5, 8, 3, 2. Se esses mesmos números forem digitados como 2, 3, 5, 8 a fechadura não abrirá. Assim, a combinação não é apenas de números, mas também a sua ordem de entrada. Se fossemos criar uma state machine para reconhecer essa sequência, ela pareceria com algo assim:

State = 0;

SWITCH (State) 

{

CASE 0: IF (in_key()==5) THEN state = 1;

Break;

CASE 1: IF (in_key()==8) THEN State = 2;

Else State = 0;

Break;

CASE 2: IF (in_key()==3) THEN State = 3;

Else State = 0;

Break;

CASE 3: IF (in_key()==2) THEN UNLOCK();

Else State = 0;

Break;

}

Prevendo que os valores retornados via in_key() estão na ordem 8, 5, 3, 2 a state variable vai do passo 0 até o 3 e a função UNLOCK() será chamada. A state variable apenas será carregada com o valor do próximo estado se o valor correto é recebido no estado correto. Se algum dos valores retornados estiverem fora da sequência, mesmo achando que ele pode ser válido em outro estado, a state variable será resetada para 0, e a state machine será reinicializada. Desta forma, a state machine somente vai efetivar a sua sequência de passos se os valores forem recebidos na mesma sequência em que os seus estados foram designados para aceitar. Assim, as state machines podem ser programadas para reconhecer uma sequência de eventos, e podem também ser programadas para gerar uma sequência de eventos. Ambos contam com o histórico dos estados anteriores e com a natureza programável das transições de estado-para-estado. 

A implementação de uma state machine é apenas uma questão de:

1.       Criar uma state variable

2.       Definir uma série de estados

3.       Decodificar a state variable para acessar aos estados

4.       Vincular ações aos estados

5.       Definir a sequência dos estados, e quaisquer condições que mude a sequência.

Por exemplo, considere uma state machine designada para fazer sanduíches de Pasta de Amendoim com Geleia. A sequência de eventos será:

1.       Obtenha duas fatias de pão

2.       Abra o vidro de Pasta de Amendoim

3.       Retire uma porção de Pasta de Amendoim

4.       Passe a Pasta de Amendoim na primeira fatia de pão

5.       Abra o vidro de Geleia

6.       Retire uma porção de Geleia

7.       Passe a Geleia na segunda fatia de pão

8.       Inverta o lado da segunda fatia de pão

9.       Coloque a segunda fatia de pão sobre a primeira fatia de pão

10.   Coma o sanduíche

OK, a primeira coisa a fazer é criar a state variable – vamos chamá-la de PBJ (Peanut Butter and Jelly – pasta de amendoim com geleia). Esta terá uma faixa de valores entre 1 até 10, e será definida provavelmente como CHAR. Depois, temos que definir a sequência dos passos do processo, e criar um meio para decodificar a state variable. Se levarmos cada uma dessas instruções e montá-las dentro de uma declaração CASE para manipular a decodificação da state variable, então tudo o que precisamos são as atualizações apropriadas para a state variable e a state machine está completa:

SWITCH(PBJ)

{

case 1: Get two slices.

  PBJ = 2

  break

case 2: Open peanut butter jar.

  PBJ = 3

  break

case 3: Scoop out peanut butter.

  PBJ = 4

  break

case 4: Smear on first slice of bread.

  PBJ = 5

  break

case 5: Open jelly jar.

  PBJ = 6

  break

case 6: Scoop out jelly.

  PBJ = 7

  break

case 7: Smear on second slice of bread.

  PBJ = 8

  break

case 8: Invert second slice of bread.

  PBJ = 9

  break

case 9: Put second slice on first slice of bread.

  PBJ = 10

  break

case 10: Eat

  break

Default: break

}

A rotina de chamada então simplesmente chama a subrotina por 10 vezes e o resultado é comer o sanduíche. Mas, porque ter todo esse trabalho? Não seria mais simples e fácil apenas escrever isto numa única longa função? Bem, sim, a rotina pode ser feita como uma longa sequência com os delays apropriados e temporizações. Mas esse formato possui um par de limitações. Primeiro, fazer um sanduíche de Pasta de Amendoim com Geleia pode ser tudo o que o microcontrolador pode fazer durante o processo. E, segundo, fazer um tipo desse sanduíche pode ser tudo que essa rotina pode ser capaz de fazer.

Existe uma importante distinção entre essas duas sentenças. A primeira afirma que o microcontrolador só seria capaz de realizar uma tarefa (task), e não multitarefa (multitask), e a segunda afirma que o programa apenas seria capaz de fazer um único tipo de sanduíche de Pasta de Amendoim com Geleia, sem variações. Quebrando a sequência acima numa state machine significa que podemos colocar outras funções entre as suas chamadas. Outras chamadas podem abranger detalhes tais como a monitoração de porta serial, verificação de um timer, ou a varredura de um teclado. Quebrando a sequência acima numa state machine também significa que poderemos usar a mesma rotina para fazer um sanduíche de apenas Pasta de Amendoim, pelo simples carregar a state variable com o estado 8, ao invés do estado 5, ao final do estado 4.

De fato, se incluirmos outros passos como derramar leite e pegar uma bolacha, e incluirmos algumas mudanças condicionais na state variable, teremos agora uma rotina que poderá fazer diversas variedades diferentes de refeições, e não apenas sanduíches de Pasta de Amendoim com Geleia. A força da arquitetura da state machine não se limite apenas a variações de sequência. Pelo controle da sua própria state variable, a state machine pode transformar-se numa forma de microcontrolador virtual especializado – basicamente um pequeno software-based-controller com um conjunto de instruções programáveis. Novamente, a força e a flexibilidade da state machine será a base para o sistema multitarefa (multitasking system) discutido adiante. Antes disso, dividi o assunto em alguns dos mais importantes conceitos, importantes para o entendimento da operação básica da state machine.

A melhor forma de iniciar isto é com os três tipos básicos de state machines: a de execution-indexed, a data-indexed e a hybrid-state-machine. A execution-indexed é o tipo de state machine que a maioria das pessoas vislumbra quando falam a respeito de state machines, e é o tipo demonstrado nos exemplos anteriores. Ela tem uma declaração de estrutura CASE com uma rotina para cada CASE, e a state variable que controla cada estado é executada quando a state machine é chamada. Um bom exemplo de uma execution-indexed é a state machine que se propõe a fazer sanduíches de Pasta de Amendoim com Geleia no exemplo anterior. A função realizada por essa state machine é especifica pelo valor retido na state variable.

O outro extremo é a data-indexed. Ela é provavelmente a forma menos conhecida de uma state machine (embora seja utilizada por muitos designers de software), em razão dela não utilizar de declaração SWITCH/CASE. Em vez disso, ela utiliza uma variável array tendo uma state variable provendo um indexador dentro da array. O conceito por trás da data-indexed state machine é que a sequência de instruções permanece constante e os dados afetados pelo controle da state variable.

A hybrid-state machine combina os aspectos das state machines descritas anteriormente para criar uma state machine com a habilidade de variar tanto a sua sequência de execução de estados, como os dados que ela opera. Esta abordagem híbrida permite variada execução de execution-indexed com o aspecto de dados variáveis da data-indexed. Temos então três diferentres formatos, com diferentes vantagens e desvantagens. A execution-indexed permite ao designer variar as ações realizadas em cada estado e/ou responder a sequências externas de eventos. A data-indexed permite ao designer variar os dados envolvidos em cada estado. E, finalmente, a hybrid que combina ambas para criar uma mais eficiente state machine, a partir do uso das características das anteriores. Dê uma boa estudada nesses três tipos de state machines e nas suas capacidades. 

State-Machine (ou Máquina de Estados) é um recurso de programação recomendado em sua maioria em sistemas microcontrolados onde não existam tasks críticas em termos de tempo de execução. Trata-se de um design de controle onde existem finitos estados conhecidos nas tasks a serem executadas.

Trata-se portanto de um tipo de controle de fluxo não muito flexível para o designer programador, visto que a exclusão, inclusão ou alteração das tasks do programa tornam-se tarefas praticamente inviáveis, em razão da grande amarração entre essas tasks e suas funções.

 

Fig. 1: Representação de uma Finite-State Machine. Este exemplo mostra como determinar se um número binário possui um número par ou ímpar de zeros, que é a condição de aceitação no teste.

O fato desse tipo de controle tornar-se em algum momento de difícil administração também é resultado do tipo de estrutura de software adotada pelo programador, em muitos casos. A utilização de funções extensas e que envolvem muitas operações num mesmo trecho do código é um grande mal, por si só.

Todavia, esse mal multiplica-se quando tais tratamentos longos - do tipo cálculos e leituras ocorrem dentro de uma ISR. Como resultado, terá a perda de controle do fluxo, bugs causados pelo uso intenso de variáveis e chamadas externas à ISR durante a sua execução, além das incertezas que esse tipo de estratégia costuma oferecer a médio e longo prazo em relação à confiabilidade e estabilidade do software.

Para esses casos, onde existam diversas tasks a serem executadas, muitas delas críticas e tempo-dependentes, recomenda-se dividir o código em pequenas porções otimizadas e funcionais. Tais recomendações referem-se a boas práticas de programação e independem da técnica de controle de fluxo adotada pelo designer programador, diga-se de passagem.

Acredito que um meio termo interessante entre o RTOS - que necessariamente se utiliza de recursos de RAM e ROM significativos de um pequeno microcontrolador para funcionar - e a Máquina de Estados seja o Time-Sliced Multitask, uma técnica de controle de fluxo de software razoavelmente bem organizada em termos de tempos de execuções, e que pode ser lida aqui.

Forum - Postagens Recentes

Noticias Recentes

Nossa Loja

Componentes Eletrônicos Para Venda On-Line

Oops! This site has expired.

If you are the site owner, please renew your premium subscription or contact support.