Arquitetura de desenvolvimento de software – parte III

Este post faz parte da série Arquitetura de desenvolvimento de software

Nos posts anteriores falamos sobre a utilização do one-single-loop e o método de organização do firmware com interrupções. No entanto, nestas arquiteturas, a adição ou modificação de uma tarefa do sistema pode impactar negativamente nos requisitos temporais ou até mesmo inserindo erros não previstos por causa das relações entre as operações realizadas. 

Uma das alternativas é a utilização de uma modelagem do sistema que preveja as tarefas, as ligações entre elas e as ações que levam o sistema a modificar seu estado atual. Esta metodologia é conhecida como máquina de estados. Cada estado representa uma situação onde o sistema realiza uma determinada tarefa, ou um conjunto de tarefas. A mudança do estado é dada quando alguma condição for satisfeita.

Se a mudança de tarefas for extremamente rápida, o efeito resultante, para o ser humano, é de que todas as tarefas estão sendo executadas simultaneamente.

Supondo um exemplo de um sistema que possua um teclado, um display e uma comunicação serial. O display precisa ser atualizado periodicamente e por isso as atualizações são intercaladas com as outras operações. Este sistema pode ser modelado conforme a figura abaixo.

máquina de estados

Podemos notar que neste modelo, após a fase de inicialização, o sistema entra num ciclo, como na abordagem one-single-loop. Isto é comum em sistemas sequenciais. No entanto, nesta modelagem é possível que a saída de um estado possa ser baseada em alguma condição gerando mais de um caminho de saída.

Alterando a estrutura acima para que a escrita da serial só aconteça quando houver algum comando, do teclado ou da serial, temos a nova máquina de estado.

máquina de estados com estrutura de decisão

A transposição de uma máquina de estado para o código em C pode ser feita facilmente através de uma estrutura  switch-case. Cada estado será representado por um case e a mudança para o novo estado é baseado nas condições de saída do estado atual.

void main(void) 
{
	//declaração das variáveis
	char slot;
	//funções de inicialização
	InicializaSerial();
	InicializaTeclado();
	InicializaDisplay();
	for(;;){	//início do loop infinito
	//*************** início do top-slot ******************
	//**************** fim do top-slot  ******************
	
	//*********** início da máquina de estado ************
		switch(slot){

			case 0:
				if( LeTeclado() != 0){
					slot = 4; //chegou comando
				}else{
					slot = 1;
				}
			break;

			case 1:
				AtualizaDisplay();
				slot = 2;
			break;		
			case 2:
				if( RecebeSerial() != 0){
					slot = 4; //chegou comando
				}else{
					slot = 3;
				}
			break;

			case 3:
				AtualizaDisplay();
				slot = 0;
			break;

			case 4:
				AtualizaDisplay();
				slot = 5;
			break;

			case 5:
				EnviaSerial();
				slot = 1;
			break;

			default:
				slot = 0;
			break;
		}
	//************ fim da máquina de estado **************
	
	//************** início do bottom-slot *****************
	//*************** fim do bottom-slot  *****************
	} //fim loop infinito (!?)
}

Com esta arquitetura a inserção de uma  nova tarefa é feita de maneira simples, bastando adicionar outro estado, ou seja, basta inserir um case/break com a tarefa desejada. Se este estado possuir mais de uma saída, basta fazer o teste de condição e indicar qual é o próximo estado a ser executado.

Como a máquina está dentro do loop infinito, a cada vez que o programa passar pelo case, ele executará apenas um estado. No entanto, esta estrutura de código gera um efeito interessante. Como pode ser visto no código, naturalmente surgem duas regiões: o top-slot e o bottom-slot. Se algum código for colocado nesta região ele será executado toda vez, de modo intercalado, entre os slots. Pela figura da máquina de estados, percebemos que é exatamente este o comportamento que queremos para a função AtualizaDisplay(). Deste modo, podemos remodelar o código fazendo a alteração abaixo.

void main(void) 
{
	//declaração das variáveis
	char slot;
	//funções de inicialização
	InicializaSerial();
	InicializaTeclado();
	InicializaDisplay();
	for(;;){	//início do loop infinito
	//*************** início do top-slot ******************
	AtualizaDisplay();
	//**************** fim do top-slot  ******************
	
	//*********** início da máquina de estado ************
		switch(slot){

			case 0:
				if( LeTeclado() != 0){
					slot = 2; //chegou comando
				}else{
					slot = 1;
				}
			break;
		
			case 1:
				if( RecebeSerial() != 0){
					slot = 2; //chegou comando
				}else{
					slot = 0;
				}
			break;

			case 2:
				EnviaSerial();
				slot = 1;
			break;

			default:
				slot = 0;
			break;
		}
	//************ fim da máquina de estado **************
	
	//************** início do bottom-slot *****************
	//*************** fim do bottom-slot  *****************
	} //fim loop infinito (!?)
}

Esta abordagem é conhecida também como cooperative multitasking. Isto implica que as funções podem demorar o tempo que quiserem e apenas ao término de sua execução a próxima função começa a ser executada. Além disso a frequência com que cada função será executada não pode ser determinada facilmente. É necessário, portanto, conhecer o tempo de execução de cada função do sistema.

No entanto é possível transformar esta arquitetura num modelo temporizado com a utilização de um timer. Assim as frequências de execução podem ser facilmente definidas. No próximo post abordaremos essa arquitetura e a utilização de interrupções em conjunto com esta arquitetura.

Arquitetura de desenvolvimento de software

Arquitetura de desenvolvimento de software – parte II Arquitetura de desenvolvimento de software – parte IV
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
4 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Jônatas Tonholo
Jônatas Tonholo
02/01/2020 09:00

Olá, poderia atualizar as imagens deste post? Estão indisponíveis. Obrigado!

Allef Pablo Araujo
Allef Pablo Araujo
17/06/2015 09:43

Bom dia, Rodrigo!
Em primeiro lugar, parabéns pelo conteúdo, esta série de posts é realmente bem útil.
Por favor, você teria como me recomendar algum livro ou materiais extras para continuar o estudo de implementação de máquinas de estados para sistemas embarcados? Existe algum material prático com exemplos das formas de implementação, como implementação por ponteiros de função, etc?

Matheus Quick
Matheus Quick
13/03/2017 00:58

Muito bom

Home » Software » Engenharia de Software » Arquitetura de desenvolvimento de software – parte III

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: