Algoritmo do contador hexadecimal
No capítulo anterior foram apresentadas as ferramentas da Microchip para desenvolvimento de código do Atmel AtMega328p na placa Arduino. Nessa parte 3 iremos abordar o algoritmo do contador hexadecimal. Como o código já está pronto, vou misturando trechos do código para facilitar a leitura do mesmo na próxima etapa.
Se você curtir esse artigo, não esqueça de dar uns cliques nos anúncios para ver o que os anunciantes tem para te oferecer. Vou ficar muito contente.
Rotina de interrupção:
Figura 1 abaixo mostra o diagrama em blocos do algoritmo do contador.
Conforme comentado sobre os recursos do microprocessador Atmel na parte 2 dessa série, o algoritmo fará uso de dois sinais de interrupção gerados a partir dos seguintes temporizadores: temporizador de 8 bits ‘Timer 0’ para controlar o refresh do display e o temporizador de 16 bits ‘Timer 1’ para incrementar a variável ‘Contador’, de 16 bits declarada no programa.
A rotina de serviço de interrupção (ISR) trata os temporizadores da seguinte forma:
O ‘Timer 0’ está configurado para gerar um sinal de interrupção por overflow aproximadamente a cada 1/30 de segundo. Esse sinal é capturado no código pela rotina ‘ISR(TIMER0_COMPA_vect)’ que por sua vez chama a função ‘refreshDisplay();’ responsável por disparar as tarefas de atualização do display que será visto a seguir.
O ‘Timer 1’ está configurado para gerar um sinal de interrupção cada vez que atinge a marca de meio segundo. Esse sinal é capturado no código pela rotina ‘ISR(TIMER1_COMPA_vect, ISR_BLOCK)’. Essa rotina de serviço de interrupção é responsável por incrementar o registro ‘Contador’, só isso.
Repetindo para não ficar dúvida: A variável ‘Contador’ incrementa automaticamente a cada meio segundo aproximadamente.
Importante notar que ‘Timer 0’ e ‘Timer 1’ geram interrupções de maneira independente e elas podem se sobrepor. Isso não é problema nesse exemplo, mas dependendo da complexidade do programa pode ser necessário controlar o momento em que as interrupções podem ser geradas e ou devem ser tratadas. O serviço de tratamento de interrupções é controlado pelas funções ‘sei()’ e ‘cli()’. Para conhecer mais sobre o assunto, seguem links adicionais:
Mais sobre interrupções:
AVR® Interrupts
AVR® External Interrupts
AVR®: 10 ms ADC Samples Averaged Over 1 Second – Base desse programa
Atualização do display – refreshDisplay():
A função ‘refreshDisplay();’ é responsável por disparar as tarefas de atualização do display conforme passos descritos abaixo:
- Faz a leitura da variável ‘Contador’ que é um inteiro de 16 bits cujo valor hexadecimal pode ir de ‘0000’ à ‘FFFF’.
- Divide o valor do ‘Contador’ em 4 conjuntos de 4 bits, pega o primeiro conjunto de 4 bits e seleciona o primeiro dígito do display.
- Decodifica o conjunto de 4 bits para o valor correspondente em 7 segmentos.
- Atualiza o digito atual do display com o valor decodificado no passo anterior.
- Passa para o próximo conjunto de 4 bits, passa para o próximo dígito e repete os passos 3, 4 e 5 até que todos dígitos sejam atualizados.
Bloco ‘Converte para 7 segmentos’ – Karnaugh:
Este tópico fala de modo prático a respeito da solução utilizada para implementar o contador e a decodificação para sete segmentos e fornece material para quem queira se aprofundar no assunto.
O registro ‘Contador’ tem 16 bits, 4 dígitos hexadecimais e cada dígito hexadecimal é composto por 4 bits. Este tópico fala apenas como faz para transformar 4 bits de um dígito hexadecimal em 7 segmentos de um display usando o Logisim, não dá para ensinar o método de Karnaugh nesse artigo, mas vai atrás de aprender isso que a mandrakaria é forte, eu garanto.
Para ficar mais pé no chão, considere que o programa chamou a função ‘int8_t decoder_4bit_7segment(int8_t input)’.
A função pede um inteiro de 8 bits chamado ‘input’ que corresponde a um dos caracteres de quatro bits do ‘Contador’. O retorno da função é um inteiro de 8 bits determinando quais leds do display tem que ficar ligados e quais leds tem que ficar desligados, formando o caractere a ser representado. Simples assim.
Repetindo o parágrafo acima de outra forma, note que a variável ‘input’ tem o tamanho de 8 bits, mas são usados somente 4 bits dessa variável. A função ‘decoder_4bit_7segment ‘ consegue decodificar um display por vez, ela recebe 4 bits pela variável input e retorna os sete segmentos cada vez que é acionada.
A função ‘decoder_4bit_7segment ‘ resolve isso empregando a técnica do mapa de Karnaugh que é a aplicação de um método gráfico para simplificar uma equação lógica ou para converter uma tabela verdade em um circuito lógico (TOCCI; WIDMER; MOSS, 2015). Detalhes de como isso é feito está documentado no capítulo 3 e 4 da bibliografia, veja o tópico Para saber mais.
Logisim:
Na prática é utilizado o software Logisim para montar a função ‘decoder_4bit_7segment ‘, mas saiba que Karnaugh está dando uma risadinha bem irônica onde quer que esteja. O código detalhado será visto na próxima parte, por ora considere que o Logisim está instalado, que tenhamos em mente as entradas e saídas para prosseguir com a montagem da equação lógica.
De entrada temos 4 bits que vou chamar de ‘iA’, ‘iB’, ‘iC’ e ‘iD’. De saída temos 8 bits que correspondem aos segmentos ‘outA’, ‘outB’, ‘outC’, ‘outD’, ‘outE’, ‘outF’, ‘outG’ e ‘Dp’ que são ativos com nível lógico 0. Como o ponto decimal sempre ficará desligado, ele não será considerado no projeto.
O próximo passo é definir as entradas e saídas no Logisim conforme figura 2 abaixo:
Após definidas as entradas e saídas, monta-se a tabela verdade na aba tabela. De acordo com o estado dos sinais de entrada, descreve-se a saída esperada, conforme indicado na figura 3 abaixo. Lembre-se que nesse caso o nível lógico zero significa que o segmento do display irá acender. Após preencher a tabela, deve-se clicar no botão ‘Construir circuito’:
Na sequência o Logisim fará algumas perguntas, responda como quiser e o circuito lógico será montado conforme indicado na figura 4. Nesse ponto é possível simular o comportamento do circuito jogando sinais na entrada e analisando a saída. Adicione um display para facilitar a analise da saída. Se a tabela foi preenchida corretamente, o circuito irá funcionar sem problemas. Essa etapa é só para testar se a lógica está correta, se funcionar aqui, a conversão para software será tranquila.
Após confirmar que a simulação está ok, salve o projeto do Logisim pois agora é a hora de implementar as expressões lógicas em software. Clique em ‘Projeto’ -> ‘Analisar Circuito’ e vá na aba ‘Expressão’, conforme indicado na figura 5 abaixo:
Na caixa drop down Saída é possível listar a fórmula para cada um dos segmentos do display. É só copiar cada uma das sete fórmulas e as implementar no código. É uma equação para cada segmento do display. Abaixo segue um conjunto de equações do Logisim convertidas em C para acionar um display de 7 segmentos.
Embora pareça complicado à primeira vista, essa técnica é base de sistemas digitais, se aplica a qualquer sistema digital combinacional com várias entradas e saídas e dispensa qualquer tipo de mapeamento. Note que apenas 8 linhas de código foram suficientes para resolver o paranauê da decodificação do display, no lugar do mapeamento de mais de 300 linhas do SevenSeg que é discutido no tópico ‘SevenSeg.h‘.
Para saber mais:
TOCCI, Ronald J.; WIDMER, Neal S.; MOSS, Gregory L.. Sistemas Digitais: Princípios e aplicações. 11. ed. São Paulo: Pearson, 2015. 818 p. ISBN13: 978-85-7605-922-6. Capítulo 3 inteiro até capítulo 4.9 incluindo questões de revisão ou compre, leia e faça todos exercícios do livro. Tudo o que está lá no livro inteiro é possível implementar em software.
Logisim – programa para simular circuitos digitais
SevenSeg.h:
Este tópico aborda a biblioteca SevenSeg.h que faz acionamento de displays de sete segmentos via mapeamento de caractere e justifica-se o motivo pelo qual a biblioteca SevenSeg não foi utilizada nesse projeto.
A maioria dos algoritmos que eu vi na Internet para representar números, letras ou símbolos em displays de sete segmentos, via de regra são por mapeamento de caractere que consiste em usar uma sequencia de ‘ifs’ ou ‘switch-cases’ para mapear quais leds do display devem ser ligados quando um determinado número, letra ou símbolo tiver que ser representado no display de 7 segmentos. Embora seja uma técnica relativamente simples, ela acaba gerando um código extenso dependendo do que se pretende resolver.
Basta verificar a biblioteca SevenSeg do Arduino. Lá no arquivo ‘SevenSeg.cpp‘ dá para ver que o código da linha 890 à linha 1208 é voltado para geração de todos caracteres do alfabeto e mais alguns sinais em um display de 7 segmentos usando a técnica de mapeamento.
Considerando que esta aplicação aqui só precisa gerar os caracteres de ‘0’ a ‘9’ e de ‘A’ até ‘F’, trata-se de um desperdício usar a SevenSeg só para isso, fora que é um despropósito criar uma biblioteca para representar o alfabeto todo em um display de 7 segmentos. É como se fosse dar uma descarga d’água num pinico de 8 bits. Melhor seria largar de miséria e partir logo para um display LCD!
Ainda pode-se argumentar que essa biblioteca SevenSeg é versátil, suporta múltiplos displays, ponto decimal, vírgula, anodo ou catodo comum, taxa de refresh automática, supressão de zero à esquerda, bate palminha e faz café. Embora ela seja ótima, e é mesmo com todos esses e outros recursos, a ideia aqui é montar um programa que apresente o conteúdo de um contador em um display, sem grandes funcionalidades (princípio KISS).
O bloco ‘Converte para sete segmentos’, em particular e conforme já dito, demanda um decodificador que leia 4 bits e apresente os números de ‘0’ a ‘9’ e letras de ‘A’ a ‘F’. Isso foi resolvido usando o Logisim. Toda a parte de temporização foi equacionada com o uso de IRQs e temporizadores nativos do microprocessador, o programa não é feito para ter controle de brilho ou servir diversos tipos e configurações de displays. É simples, não faz sentido usar a biblioteca SevenSeg neste projeto.
Próxima etapa
Conforme abordado nessa parte, conhecer tópicos como lógica digital, método de Karnaugh e saber aplicar esses conhecimentos em programação, dá ao desenvolvedor uma ferramenta a mais para facilitar sua profissão. Nesse exemplo foi mostrado que basta identificar as entradas, as saídas, construir a tabela verdade e montar as equações lógicas, simplificando-as pelo método de Karnaugh ou usando a ferramenta Logisim para em seguida aplicar o resultado diretamente no código. Também foi explicado o motivo pelo qual não se adotou a biblioteca SevenSeg do Arduino.
Na próxima etapa dessa série, o código completo será detalhado e serão abordadas algumas particularidades da interface de desenvolvimento do Atmel Studio. Até a próxima etapa e me aguarde!
By Renato de Pierri,
20/05/2019
oi então… bem legal a lógica ali, mas porque não usar uma tabela estática com 16 bytes? (1 bit de cada byte sendo um segmento), que resulta em código+data menor ainda?
Melhor ainda sua sugestão, Greg!
A ideia aqui foi eliminar a biblioteca original, que foi obtido com facilidade.
Faz uma gentileza, compartilha como ficaria o código utilizando esse conceito que eu acrescento na solução.