Controle de Display de 7 segmentos (arduino)

Tenho um circuito com um 3 displays de 7 segmentos, 1 decodificador e 3 botões

O intuito é digitar uma senha de 3 dígitos: o primeiro botão faz o número cair, o segundo faz o número crescer e o terceiro botão (B3) vai pro próximo dígito

Alguém tem uma ideia de como especificar qual display será controlado por vez?

Então quero primeiro digitar o dígito do primeiro display (os outros permanecem no 0)

Quando B3 é pressionado, quero ir para o segundo display (o primeiro permanece no dígito que foi colocado no passo anterior)

E ao pressionar B3 novamente, enfim é inserido o dígito do terceiro display

Queria conseguir fazer isso sem usar 21 pinos do arduino

Estou montando o circuito em um simulador, estou deixando abaixo a figura de como está no momento (ainda tenho apenas o "esqueleto" do circuito, porque travei pensando nesse problema)

Exibições: 858

As respostas para este tópico estão encerradas.

Respostas a este tópico

Boa noite EV

dê mais detalhe sobre o seu projeto para que possamos ajudar melhor.

1. Qual arduino você está usando?

2. Qual modelo do seu display de 7 segmento?

3. Qual decodificador você tem para usar no projeto?

4. Você já tem um sketch, (mesmo que não funcione ainda)?

     Se tiver um sketch, anexe aqui como um arquivo. Não cole sketch na área de texto do tópico.

RV mineirin

Boa noite RV

Editei o tópico e acrescentei um anexo com um print do circuito

Bom dia  EV,

Este projeto é um TCC?

     Você já tem um código, (mesmo que não funcione ainda)?

     Se tiver um sketch, anexe aqui como um arquivo. Não cole sketch na área de texto do tópico.

O  7 segmento é CC ou AC?

RV mineirin

Boa noite,

Referências para estudos:

Guia completo dos Displays de 7 segmentos – Arduino

https://blog.eletrogate.com/guia-completo-dos-displays-de-7-segment...

Arduino – Keypad 4×4 e display digital

https://blog.eletrogate.com/arduino-keypad-4x4-e-display-digital/

Guia completo do Shield Multi-funções para Arduino

https://blog.eletrogate.com/guia-completo-do-shield-multi-funcoes-p...

Boa noite,

Segue um circuito para complementar o seu.

https://forum.arduino.cc/t/multiplexing-7-segment-displays-with-a-4...

Se precisar de algo, é só ver o artigo.

Boa sorte e saúde,

  'Eiju

olá Ester.

 

      Estou publicando uma implementação para este Sistema que vc descreveu e pediu ajuda aqui. Como vc disse que não sabe por onde iniciar, então reuni as informações que vc passou (por exemplo, uso do Decoder 4511), e por este motivo a implementação é bastante completa permitindo que seja desenvolvida na direção que vc ou outros desejem. Esta também é uma ótima oportunidade para mostrar a implementação de coisas que podem até parecer complexas a princípio, mas que são na realidade bem simples. Para isto, o Hardware é descrito em detalhes, e o código está totalmente organizado (de forma hierárquica) e comentado funcionalmente (o que é indispensável para se entender sem esforço, como ele funciona). Mesmo assim irei descrever aqui, alguns pontos principais para aqueles que não querem se aprofundar e desejam apenas utilizar os recursos. Aconselho a quem for utilizar, ler integralmente o texto aqui publicado.

 

      Implementei também uma Simulação no Proteus, a qual permite testar o funcionamento do Sistema e também medir alguns parâmetros importantes. Sobre a Simulação, alguns aspectos são apresentados mais adiante.

 

      O código implementado e os arquivos para a Simulação, estão disponíveis para download no final deste post (há também um vídeo mostrando a execução da simulação).

  

 

      Sobre o Circuito

 

      A implementação do Hardware usa o Arduino UNO, o Decoder CMOS de 7 segmentos 4511 (mais "corretamente" o 4511B), e três Displays de 7 segmentos de Katodo Comum (já que o 4511 permite acionar diretamente esse tipo de Display). Há também os três Botões usados para inserção da Senha, e dois LEDs que indicam se a Senha inserida está correta ou não. Os demais componentes são Resistores e Transistores. O circuito implementado pode ser visto na figura a seguir:

 

 (clique na figura para "zoom")

  

 

      Vamos falar um pouco então sobre os elementos usados neste Hardware:

  

      1)  Arduino:  está sendo usado o UNO (uma vez que foi este o Arduino informado no tópico em questão), e isto facilita muito "traduzir" este Sistema para outros Arduinos. Conforme pode ser visto na figura anterior, são usados 4 pinos para especificar um valor de 0 a 9 para o Decoder 4511, sendo estes pinos o 4, 5, 6, e 7, conectados respectivamente aos bits "A" (ou bit "0"), "B" (ou bit "1"), "C" (ou bit "2"), e "D" (ou bit "3") do 4511. Mas poderiam ser usados quaisquer pinos de saída Digital do Arduino e em qualquer ordem desejada, pois o código implementado permite isso.

      Para o controle ON/OFF de cada Display, foram usados os pinos "A1", "A2", e "A3" do UNO. Aqui também, quaisquer pinos podem ser usados e em qualquer ordem.

      Para leitura dos estados dos três Botões e controle dos dois LEDs, outros 5 pinos são usados, e novamente podem ser especificados quaisquer outros pinos.

      Assim ainda restam disponíveis 8 pinos do Arduino UNO. Observar que os pinos restantes permitem que se use algumas funções especiais, como pelo menos uma entrada do ADC (pino A0), Interface I2C (pinos A4 e A5), Interrupções Externas (pinos 2 e 3, mas há também as Interrupções do tipo "pin change" para outros pinos), PWM (pino 3), além da conexão ao Terminal do Arduino via Serial 0 (pinos 0 e 1). Claro, todos os 8 pinos também podem ser usados para I/O convencional. Então quando vc escolher qualquer outra combinação de pinos para o Sistema, analise antes qual é a que melhor se encaixa para suas necessidades.

  

      2)  Decoder 7 segmentos e Displays:  está sendo utilizado o 4511. Este Decoder embora seja CMOS, possui internamente em cada uma de suas 7 saídas para os LEDs de segmentos, um Driver com Transistor Bipolar para fornecer a corrente para os respectivos LEDs (claro, via Resistor externo para limitar e determinar a corrente de saída). É um Decoder para Displays de Katodo Comum. As entradas "LT" (Light Test), e "BI" (Blanking Input), são ativas em "0" e como não estão sendo usadas, são ligadas a "1" (os 5V da alimentação). A entrada "LE" (Latch Enable) deve ser ligada ao "0" (GND do circuito), para que o Latch interno do 4511 fique "transparente", ou seja desabilitado, para que as saídas de 7 segmentos sempre estejam reproduzindo o valor binário presente nas entradas A, B, C, e D.

      Os Resistores conectados às saídas QA a QG do 4511, determinam a corrente para cada segmento. Para os valores no circuito, 330 Ω, a corrente será algo próximo a 10mA. Para este cálculo, desconte de 5V, a tensão sobre um LED de segmento, e então terá a tensão sobre o Resistor. No caso, vamos considerar que o LED do Display tem uma tensão Anodo/Katodo de 1.5V quando ligado. Então sobre o Resistor teremos 5V - 1.5V = 3.5V (aqui estamos "desprezando" a queda no Transistor Bipolar interno do 4511). Então a corrente será 3.5V / 330 Ω ≈ 10.6 mA.  Da mesma forma, pode-se também calcular o Resistor, especificando-se a corrente desejada, mas evite passar de 20mA, a fim de limitar o total de corrente fornecido pelo 4511 (veja o datasheet do mesmo para mais detalhes). Caso correntes bem maiores sejam desejadas, pode-se usar Transistores adicionais (tipo NPN) nos pinos de segmentos.

      Observar que estamos considerando que os Transistores Q1 a Q3 estão funcionalmente saturados quando acionados pelas saídas do Arduino. Isto é fácil de garantir, supondo-se um HFE médio (por exemplo 100) e escolhendo-se uma corrente de base em torno de 1 mA. Caso alguém queira saber sobre estes cálculos, pergunte aqui neste mesmo tópico, ou me contate via email do LDG.

 

      Nota Importante:  é possível dispensar Q1 a Q3 e R11 a R13. Para isso basta que se ligue as saídas do Arduino (no caso pinos A1, A2, e A3), diretamente aos Katodos dos Displays. Nenhuma alteração no código é necessária, uma vez que os Transistores usados são do tipo PNP, o que significa que o código aciona estes por Lógica "LOW" (isto foi feito propositalmente). Notar porém, que o uso dos Transistores traz uma maior flexibilidade na escolha das correntes dos segmentos dos Displays, já que a corrente confiável para uma saída do Arduino é de 20mA,  e NÃO  40mA como muitos adoram proclamar!!!  Vejam: 40mA é para os "limites máximos absolutos" e não preciso dizer mais nada. Mas há um ponto que ajuda muito aqui:  os Displays são acionados de forma multiplexada, e sempre há apenas um único Display acionado de cada vez. Isso significa que a corrente média total de um Display será 1/3 (um terço, já que são três Displays) da corrente determinada pelos Resistores dos segmentos. Como temos 7 segmentos, se a corrente individual é de 10 mA, então a corrente total seria 7 x 10mA = 70mA quando todos os segmentos estiverem ligados (reproduzindo o número "8"), mas a corrente efetiva seria 1/3 disso, ou seja: 70mA / 3 = 23mA.  Claro, isso é ligeiramente acima da corrente "segura" para um pino do UNO, mas considerando-se que dificilmente todos os segmentos estarão ligados nos três Displays, então esse limite marginal pode ser desconsiderado. Mas de todo caso, o uso dos Transistores  Q1 a Q3  é mais sensato, garantindo inclusive que não se extrapole a máxima corrente de todos os pinos somados do processador do UNO (o AtMega328).

  

      3)  Botões:  para estes, estão sendo utilizados os Pullups internos do Processador do UNO, o que dispensou o uso de Resistores externos. A Lógica implementada no código faz o "debouncing" dos Botões automaticamente (e sem travar a execução do código). Isso permite dispensar Capacitores externos para o "debouncing" (e eventuais Diodos de Proteção, necessários se os Capacitores tivessem valores relativamente altos).

  

      Sobre a Varredura dos Displays e dos Botões

 

      Como pode ser visto na figura inicial que mostra o circuito do Sistema, os Displays são acionados por meio da varredura dos mesmos, ou seja, a exibição dos valores em cada um dos três Displays, é feita de forma multiplexada. Logo, somente um Display está efetivamente "ligado" num determinado momento. A frequência de varredura (ou taxa de "Refresh"), foi escolhida como sendo 50 Hz. Vamos entender os motivos disso. Primeiro, deve ser uma frequência que não permita a retina do olho humano acompanhar o liga/desliga dos Displays, dando a impressão que todos os três Displays estão sempre ligados, e portanto quanto maior esta frequência, melhor. E para que as demais funcionalidades do código sejam executadas sem haver preocupação em manter a taxa de varredura do Display, essa varredura é executada em uma rotina de Interrupção (ISR), e para gerar essa Interrupção em uma taxa constante, é utilizado o "TIMER1" do Hardware do Processador do Arduino. Assim na inicialização do Sistema, o código programa o "TIMER1" para gerar a taxa de varredura dos Displays. A cada interrupção, é feita a varredura de um único Display. Logo, para varrer os três Displays a 50 Hz, é necessário uma taxa de Interrupção de 3 x 50Hz =  150 Hz. Os detalhes do código que fazem essa varredura são mostrados mais adiante. Observar que a ISR (rotina de Interrupção) deve ser eficiente, para garantir que as demais funcionalidades do Sistema sejam executadas de forma "fluida", ou seja, sem parecer que as Interrupções estão ocorrendo 150 vezes por segundo.

 

      No código implementado, a varredura dos Displays de fato é muito eficiente, e consome efetivamente pouco tempo. Por este motivo, para tornar o Sistema também "despreocupado" com a leitura e gerenciamento dos Botões, essa tarefa também é executada na mesma rotina de Interrupção do "TIMER1". Essencialmente, ali é feita a leitura dos estados dos Botões e conforme estes estados, é determinado se um Botão foi acionado, desacionado, ou se está pressionado por um determinado tempo. O código implementa isso de forma enxuta, longe de sacrificar tempo de processamento do restante do Sistema.

      Se forem acrescentados ao Sistema dispositivos "One Wire" (ex.: Sensor DS18B20) ou semelhante (ex.: DHT11 ou DHT22), alguns cuidados devem ser tomados para se garantir os tempos de "slot" para estes dispositivos. Mas é algo perfeitamente factível, e posteriormente posso mostrar como fazer isso (há pelo menos três formas de implementação).

      Para dispositivos I2C, nenhum cuidado especial é necessário, uma vez que estes também são cadenciados por IRQ (Interrupt Request) e não há um momento específico para que estas IRQs sejam atendidas.

      Quaisquer dúvidas relacionadas, posso responder aqui mesmo neste tópico.

 

 

      Sobre o Código e Funcionamento do Sistema

 

      A Senha do Sistema é definida no início do código, conforme mostrado na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Há no Sistema dois LEDs, um Verde (ou LED "OK") e um Amarelo (ou LED "ERRO"). O LED Verde é utilizado para sinalizar quando a Senha correta é inserida, e o Amarelo indica quando a Senha inserida está incorreta. A definição no código para a configuração de Hardware dos LEDs, pode ser vista na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Três Botões controlam a operação do Sistema. O Botão "1" decrementa o valor exibido no Display atualmente selecionado. O Botão "2" incrementa o valor exibido. Já o Botão "3" tem várias funções, sendo a mais comum, selecionar o próximo Display para entrada da Senha.

 

      O funcionamento do código é bastante simples: enquanto no "background" a varredura dos Displays e dos Botões é executada quando as Interrupções do TIMER1 ocorrem (na taxa de 150 Hz), temos que no "foreground" (ou execução "normal") o código decrementa ou incrementa o valor exibido no Display atualmente selecionado conforme acionamento dos Botões "1" ou "2". E quando o Botão "3" é acionado, então o código simplesmente seleciona o próximo Display.

 

      Para cada um dos três Displays há uma variável do tipo "byte", que pode assumir valores de 0 a 9. São exatamente o valor dessas três variáveis que são exibidas pelo mecanismo de varredura do Display (no "background", ou seja, na rotina de Interrupção).  E no início de tudo, as três variáveis estão zeradas, o que resulta em exibir "000" nos Displays. Há também um variável do tipo byte que indica qual Display está atualmente selecionado, e a mesma assume valores de 1 a 3.  E no início, esta variável assume o valor "1", selecionando assim o Display "1" para a entrada de um Dígito da Senha (neste caso o Dígito das centenas).  A figura a seguir mostra as três variáveis para os valores exibidos em cada Display, além da variável que indica qual Display está atualmente selecionado:

 

(clique na figura para "zoom")

 

 

      Se um Display está no valor "0" e é decrementado, ele "recicla" para "9". E se um Display está no valor "9" e é incrementado, ele recicla para "0". Então o que o código tem que fazer é verificar quando cada Botão é acionado e aplicar as ações descritas.

      Mas quando o Display "3" está selecionado e o Botão "3" é acionado, isso deve ser interpretado como "fim da entrada de Senha", e o Sistema então deve verificar se a Senha mostrada nos Displays, corresponde à Senha do Sistema.

 

      Se a Senha que foi "inserida" via Displays estiver correta, então o LED Verde (LD1 no circuito) acende. Mas se a Senha estiver incorreta, então o LED Amarelo (LD2) acende.

 

      Após a verificação da Senha, o Sistema fica aguardando que o Botão "3" seja novamente acionado, para reiniciar uma nova entrada de Senha. Neste ponto, os dois LEDs apagam, sendo selecionado o Display "1" para entrada de um Dígito da Senha, e os Displays exibem "000", e portanto o Sistema volta ao "início".

 

      A forma mais simples e confiável de implementar esse sequenciamento do código, é através de uma Máquina de Estados, que é mostrada mais a frente neste post. Apenas 3 estados foram necessários para implementar todo o processamento.

 

 

      Há no código um bloco que trata de todo o gerenciamento de leitura dos Botões, e que é executado no "background", ou seja, na rotina de Interrupção do TIMER1 (onde também é feita a varredura dos Displays). Como dito anteriormente, este bloco de código verifica se um Botão foi acionado ou desacionado, e também "mede" o tempo que um Botão está acionado. Estas informações estão disponíveis para serem usadas no "foreground" do código permitindo assim que este tome as ações conforme os estados dos Botões. Mas para que isto seja possível, é necessário que os Botões sejam definidos no Sistema. Esta definição é simples e consiste de duas etapas. A primeira é a definição dos pinos de Hardware onde os Botões estão conectados, conforme mostrado na figura a seguir:

 

(clique na figura para "zoom")

 

 

      A segunda etapa é a criação de estruturas de dados para armazenar os status dos Botões, e de funções que configuram estes Botões e atualizam os status dos mesmos. Isto pode ser visto na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Caso fossem incluídos mais Botões no Sistema, bastaria se estender a mesma ideia mostrada na figura, acrescentando as definições para estes Botões juntamente aos já existentes.

 

      Notar que a função "atualiza_Status_Botoes" será executada no "background", ou seja, na rotina de Interrupção do TIMER1. Ela está incluída na figura anterior para se mostrar como é simples a definição completa para uso de um Botão no Sistema. Esta função será especificada como a que justamente atualiza os status dos Botões, através de um "HOOK" na rotina de Interrupção (mostrado mais adiante quando a ISR for apresentada).

 

 

      Em relação ao Decoder 4511, são necessários 4 pinos do Arduino para se especificar qual valor será exibido em um determinado momento. A definição destes pinos é mostrada na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Como já dito, quaisquer pinos podem ser especificados, e em qualquer ordem, pois o código se encarrega de setar cada um deles para reproduzir o valor binário para o Decoder 4511.

 

 

      Para o controle ON/OFF dos três Displays, 3 pinos devem ser especificados para este controle. Novamente, quaisquer pinos podem ser especificados, e em qualquer ordem. No código, esta definição pode ser visto na figura a seguir:

 

(clique na figura para "zoom")

 

 

      A figura a seguir, mostra a captura da tela da Simulação do Sistema no Proteus, em um momento que o Display "1" está sendo varrido, e por isso apenas o mesmo aparece "ligado" na figura exibindo o valor "3":

 

(clique na figura para "zoom")

 

 

      Como a taxa de varredura total é de 150Hz (ou seja, 3x 50Hz), então o período de varredura é de 6.67ms (ou seja, 1 / 150Hz). Logo a cada 6.67ms o próximo Display é ligado, sendo setado em binário nos pinos  A..D  do 4511, o valor a ser exibido naquele Display. Ou seja, cada Display fica "ligado" justamente por 6.67ms, e fica desligado por 13.33ms, o que resulta em um ciclo de 20ms, implicando na Frequência individual de 50Hz para cada Display.

 

      Obs.:  a captura da figura anterior foi facilitada pelo fato de que a Simulação executa em um tempo "virtual", completamente independente do tempo real, e o Simulador aumenta ou diminui o step de tempo conforme detalhes da Simulação são executados. Por isso mesmo, quando se executa a simulação, é possível acompanhar partes do processo de varredura, o que causa um efeito que pode parecer "estranho", já que nunca vemos todos os três Displays ligados ao mesmo tempo. Eu disse "partes do processo de varredura", porque enquanto a simulação evolui, o próprio Simulador faz capturas do status desta simulação, para então "printar" estes status na tela do Simulador (aqueles pontinhos vermelhos e azuis que vemos nos prints), e essa taxa de captura não tem nenhuma relação com o conteúdo da simulação, o que resulta em um efeito sub-amostrado (é o mesmo efeito que uma luz stroboscópica existente em pistas de dança das danceterias provoca em nossa retina, resultando em algo semelhante a um vídeo onde faltam cenas). Isto pode ser melhor visto no vídeo da simulação mostrado no final deste post (resulta em um "pisca/pisca" dos valores exibidos no Display, e claro isso é apenas na Simulação).

 

      Como já dito, a taxa de varredura total deve ser 3 vezes a taxa desejada para um único Display. Assim como desejamos 50Hz para cada Display, a taxa total de varredura deve ser 150Hz, e isso é definido no código conforme mostrado na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Em uma das simulações, acrescentei um Frequencímetro para medir a frequência de varredura para cada um dos Displays, e o resultado pode ser visto na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Como pode ser visto na figura anterior, acrescentei também uma chave rotativa (no circuito é a SW1), que permite selecionar em qual dos Displays estaremos medindo a taxa de varredura. Para todos foi medido 50Hz, como seria de se esperar.

 

(devido à limitação de espaço este post continua logo a seguir:  Link)

(continuação do post anterior, iniciado neste Link)

 

      Vejamos agora alguns detalhes a respeito da Interrupção usada para fazer a varredura dos Displays e dos Botões. Esta ISR ("Interrupt Service Routine" = Rotina de Serviço da Interrupção), é gerada pelo TIMER1, que é programado logo no início da execução do código, para gerar uma IRQ ("Interrupt Request" = Requisição de Interrupção)  150 vezes por segundo (ou a cada 6.67 mili-segundos). Uma observação: o TIMER1 do Processador do UNO, tem 4 fontes de Interrupção diferentes, e destas 4 fontes 3 poderiam ser utilizadas para gerar as IRQs para cadenciar a varredura do Display, mas neste Sistema estamos usando a Interrupção gerada pelo COMPA ("Compare Match A").

      A programação do TIMER1 para gerar as IRQs é feita na função "config_Display_Refresh", e embora esta seja extremamente simples, também é muito específica já que programa o Hardware do TIMER1, e por isso não irei falar sobre ela aqui (o código está funcionalmente comentado e descreve as operações realizadas). Mas caso alguém tenha alguma dúvida, pergunte aqui mesmo neste tópico.

 

      A ISR do "TIMER1 COMPA" é mostrada na figura a seguir:

 

(clique na figura para "zoom") 

 

      Esta ISR  tem três etapas:

 

      1)  atualizar o Display, executando assim a varredura do mesmo, que é a área marcada na cor verde na figura anterior. Para isto é usada a variável local "DISPLAY_sel" (que é do tipo "static", e portanto retém seu valor mesmo depois que a rotina termina sua execução), a qual indica qual Display será "ligado" neste momento (e por 6.67ms quando a próxima IRQ ocorrer). Quando a ISR é executada pela primeira vez (e apenas na primeira vez!), "DISPLAY_sel" estará com o valor "1", indicando que o Display "1" será "ligado" e exibirá o valor correspondente ao mesmo. Então conforme cada statement "case" do "switch" é chamada a função "set_Digito_Display" passando como parâmetros a variável correspondente ao Display atualmente sendo "ligado" (ou "SENHA_IN_1", ou "SENHA_IN_2", ou "SENHA_IN_3"), e também o número correspondente ao Display (ou "1", ou "2", ou "3"). Assim esta função "set_Digito_Display" irá setar nos pinos correspondentes aos bits "A", "B", "C", e "D" do Decoder 4511, o valor binário correspondente ao valor a ser exibido (dado por "SENHA_IN_1", "SENHA_IN_2", ou "SENHA_IN_3"), mantendo ligado apenas o Display especificado ("1", "2", ou "3"), e deixando desligado os demais. Mais à frente veremos como a função "set_Digito_Display" faz isso.

 

      2)  selecionar o Display que será "ligado" na próxima execução da ISR (que ocorrerá daqui a 6.67ms), que é a área marcada na cor azul na figura anterior. Para isso, simplesmente é incrementada a variável "DISPLAY_sel". Mas como esta variável deve ir de 1 a 3, se o incremento ultrapassar "3", então a variável é reciclada para o valor "1", assim selecionando o Display "1" para a próxima IRQ.

 

      3)  executar o "HOOK" correspondente a esta ISR, que é a área marcada na cor amarela na figura anterior. Um "HOOK" nada mais é do que uma chamada a uma função "genérica", e esta função pode ser alterada a qualquer momento pelo código que controla o Sistema. No entanto, neste Sistema este "HOOK" é setado uma única vez, logo no início da execução do código, para apontar a função "atualiza_Status_Botoes", e assim mantido durante todo o funcionamento do Sistema (ou seja, o "HOOK" não é mais alterado). Portanto, na área marcada na cor amarela, simplesmente será chamada a função "atualiza_Status_Botoes", a qual fará a varredura dos Botões  e atualizará os status dos mesmos. A função que seta este "HOOK" é a "config_Botoes", que já foi mostrada quando falamos sobre a configuração dos Botões. Todas estas funções são extremamente simples e estão muito organizadas, então vale a pena vc dar uma olhada nas mesmas, pois certamente o aprendizado será significativo.

 

      Então após a execução destas 3 etapas, a ISR termina, e o Processador volta a executar o código "normal" no "foreground". A ISR será executada repetidamente, a cada 6.67ms, onde o próximo Display será ligado para exibir seu valor correspondente, e também será feita a varredura dos Botões para a atualização dos status dos mesmos.

 

 

      Vamos dar uma olhada rápida na função "set_Digito_Display", a qual recebe como parâmetros o valor a ser exibido num dos Displays e o indicador de qual é este Display. Esta função pode ser vista na figura a seguir:

 

(clique na figura para "zoom")

 

 

      O valor a ser exibido é especificado através do parâmetro "DIGITO_N", o qual deve estar entre "0" e "9", uma vez que corresponde a um Dígito da Senha. Por este motivo, o Display só será atualizado se o valor do Dígito estiver dentro da faixa correta (menor ou igual a "9"), o que pode ser constatado no statement "if" na linha 541 na figura anterior.

      Mas antes disso, temos a execução da função "set_Display_OFF", cujo objetivo é desligar todos os Displays, o que é feito através dos pinos que controlam os Transistores Q1, Q2, e Q3. A função "set_Display_OFF" é extremamente simples e pode ser vista na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Então no início da função "set_Digito_Display" temos que todos os três Displays são desligados. Portanto se o valor do "DIGITO_N" estiver fora da faixa permissível, como resultado temos que todos os Displays desligam, o que de certa forma nos sinaliza que alguma coisa está errada no código. Isto pode ser entendido quase como um teste de sanidade. Em outras palavras, o teste "if" pode ser eliminado, desde que seu código mantenha os valores dos Dígitos dentro da faixa correta. Mas a boa prática de programação, aconselha que se mantenha o teste "if" da linha 541.

 

      Na figura que mostra a função "set_Digito_Display", existem 4 áreas marcadas na cor verde. Cada uma dessas 4 áreas, testa um bit do valor "DIGITO_N", e reproduz esse bit nos pinos "A" a "D" do Decoder 4511. Dessa forma, o valor do "DIGITO_N" será traduzido pelo Decoder 4511, para seu equivalente em 7 segmentos. O teste para saber se cada bit está em "0" ou "1" é feito através da operação AND (o "&" da Linguagem C/C++) com os valores 0x01 (para teste do bit "0"), 0x02 (teste do bit "1"), 0x04 (teste do bit "2"), e finalmente 0x08 (teste do bit "3"). Então conforme o resultado destes testes, ou "HIGH" ou "LOW" será setado nos pinos "A" a "D" do 4511. Ou seja, a reprodução direta e simples de um valor binário de 4 bits.

 

      Mas em qual dos Displays este valor aparecerá?  Isto é dado pelo parâmetro "DISPLAY_select", que especifica a qual dos Displays corresponde o valor "DIGITO_N". Logo o parâmetro "DISPLAY_select" pode assumir valores de 1 a 3, indicando assim em qual Display "aparecerá" o valor decodificado pelo 4511. Dentro do statement "switch" (na linha 575), há 3 áreas marcadas na cor azul, e cada uma testa se "DISPLAY_select" é um dos três Displays. Como se vê pela lógica extremamente simples, somente o Display especificado pelo parâmetro "DISPLAY_select" será ligado. E a função termina.

 

      E o processo de varredura continua indefinidamente, na taxa de 150 Hz.

 

      Aqui, para não demorar mais, não irei falar sobre detalhes da leitura/processamento dos status dos Botões (embora a lógica ainda seja simples). Mas qualquer dúvida é só perguntar.

 

      Mas há um ponto muito importante a verificar. Quanto tempo todas as operações executadas na ISR, levam para serem executadas???  A forma mais simples de verificar isso é medindo. Então o que eu fiz foi usar um pino "sobrando" no UNO para gerar um pulso digital  que corresponda à praticamente o tempo de duração da ISR. No caso, usei o pino 8, embora isso não apareça no circuito nem no código, pois fiz isso em uma cópia do Projeto apenas para a medição. Simplesmente, a primeira coisa que é feita na ISR, é setar o pino 8 em "HIGH", e a última coisa feita na ISR é setar o pino em "LOW". Isto fornece excelente precisão na medição do tempo gasto na ISR. O resultado pode ser visto na figura a seguir, capturada do Osciloscópio "virtual" do Proteus:

 

(clique na figura para "zoom")

 

 

      Como se vê na figura anterior, são gastos cerca de 80 µs na ISR, e isto inclui a varredura do Display e dos três Botões. E qual percentual este tempo corresponde em relação ao tempo que sobra para a execução do código "normal" (no "foreground")???   Simples: basta dividir 80µs por 6.67ms (o ciclo da ISR = 1 / 150Hz), e então multiplicar por 100%, e teremos o percentual desejado. Vejamos então:  80µs / 6.67ms = 0.012,  e o percentual será 100% x 0.012 = 1.2%.  Ou seja, arredondando temos que  todo  o processamento da ISR corresponde a cerca de 1%  do processamento total do Sistema. A coisa então é muito eficiente (mas como eu disse no início do post, algum cuidado especial deve ser tomado se forem acrescentados ao Sistema dispositivos "OneWire" ou semelhantes a DHT11 ou DHT22").

 

      Aproveitando, pode-se verificar o ciclo da ISR, bastando apenas ajustar a escala de tempo do Osciloscópio "virtual" do Proteus, conforme pode ser visto na figura a seguir:

 

(clique na figura para "zoom")

 

 

      E conforme esperado, o ciclo é de 6.67ms, correspondente aos 150 Hz.

 

 

      Sobre a inicialização do Sistema

 

      Dado ao objetivo deste tópico, não há muito para falar sobre a inicialização do Sistema. Mas qualquer dúvida sobre este processo, basta perguntar. Vejamos a função "setup" do Arduino, que é obrigatória nesta Plataforma, e é mostrada na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Ali, há apenas três funções de inicialização sendo executadas.

 

      A primeira é a "config_LEDs", que simplesmente configura como saídas, os pinos dos LEDs "OK" e "ERRO", e também garante que inicialmente estes LEDs estejam desligados.

 

      A segunda é a "config_Botoes", já mostrada aqui no post. Ela configura como entradas, os pinos dos Botões, e inicializa os status de cada Botão. Também nesta função, como já dito, é setado o "HOOK" existente na ISR do "TIMER1 COMPA" para que seja feita a varredura dos Botões e atualização dos status dos mesmos (via função "atualiza_Status_Botoes").  Observar que, o "HOOK" só será executado após a configuração do TIMER1, o que é feito logo a seguir.

 

      A terceira e última função no "setup", é a "init_Display_Control". Ela primeiro configura e inicializa todos os pinos relacionados com o controle dos Displays (os pinos conectados ao Decoder 4511 e os de ON/OFF dos Displays via Transistores Q1 a Q3 do circuito). Então ela configura o TIMER1 para gerar as Interrupções que cadenciam a varredura dos Displays na taxa de 150Hz (incluindo a execução do "HOOK" para tratamento dos Botões).

 

 

      Sobre a entrada e verificação da Senha

 

      A inserção (ou entrada) da Senha, é controlada através de uma Máquina de Estados, e somente 3 estados  foram necessários para todo o controle.

 

      Mas antes de falar sobre a execução desta Máquina, vamos ver duas funções muito simples, que são utilizadas para decrementar ou incrementar  os valores dos dígitos da Senha quando a mesma está sendo "inserida" via Botões "1" e "2". Também veremos a função que verifica se a Senha inserida está correta. Estas funções são mostradas na figura a seguir:

 

(clique na figura para "zoom")

 

 

      A primeira função, de nome "decrementa_Digito_Senha", irá decrementar o valor do parâmetro "valor_DIGITO". Notar que o valor é do tipo byte, sendo que o "&" logo após a palavra "byte" indica que o parâmetro é passado para a função "por referência", ou seja, a função irá alterar a variável original correspondente ao parâmetro fornecido. O funcionamento é muito simples:  a função verifica se o valor é "0", e caso seja, ele é setado para "9", já que a faixa de valores deve ir de 0 a 9. Mas se o valor for outro, ele simplesmente é decrementado.

 

      A segunda função, de nome "incrementa_Digito_Senha", tem o mesmo mecanismo, porém irá incrementar o parâmetro "valor_DIGITO", também fornecido "por referência". Mas aqui é verificado se o valor é "9", e caso seja, ele é setado em "0". Do contrário, o valor é simplesmente incrementado.

 

      A terceira e última função, a "verifica_SENHA", verifica se está correta a Senha atualmente inserida. Como a Senha inserida é constituída pelos dígitos individuais dados pelas variáveis "SENHA_IN_1", "SENHA_IN_2", e "SENHA_IN_3", é preciso "juntar" esses três dígitos em um único valor e então proceder à verificação se a Senha está correta. Como "SENHA_IN_1" tem o peso das centenas, este dígito deve ser multiplicado por 100. E como "SENHA_IN_2" tem o peso das dezenas, este dígito deve ser multiplicado por 10. E como "SENHA_IN_3" tem o peso das unidades, já está com o valor adequado para constituir a Senha atualmente inserida (e que está sendo exibida no Display). Isto é feito na linha 957 da função, onde as três parcelas são somadas. Então basta comparar o valor resultante com a Senha do Sistema, o que é feito na linha 959 da função, sendo o resultado desta comparação ("true" ou "false") informado no retorno da função.

 

 

      Como dito, uma Máquina de apenas três estados, controla todo o mecanismo de entrada da Senha e verificação da mesma. Pode parecer difícil de visualizar ou acreditar nisso, mas o processo é muito simples conforme veremos a seguir.

 

      Primeiro, vamos ver a definição dos três estados da Máquina de Estados, o que é feito em uma lista enumerada  mostrada na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Notar que cada estado definido, está marcado com uma cor diferente (verde, azul, e laranja). Isto facilitará visualizar na Máquina de Estados, justamente a execução de cada um destes estados. Notar também que junto às definições, está a descrição do que é feito em cada estado, embora o próprio nome destes já deixe isso praticamente óbvio.

 

      A Máquina de Estados está implementada na função "loop" do Arduino. Vejamos sua estrutura, o que é mostrado na figura a seguir:

 

(clique na figura para "zoom")

 

 

      Observar as três regiões marcadas com as mesmas três cores correspondentes aos três estados da Máquina (definidos anteriormente na lista enumerada). Cada região corresponde à execução de um único estado. Quando a função "loop" é executada (e sabemos que ela é executada de forma cíclica), somente um dos estados será executado, e quem determina isso é a variável local de nome "estado_MAQ" (definida na área marcada na cor amarela na figura anterior), que é do tipo byte e também "static" (indicando que seu valor é mantido mesmo quando a função termina sua execução). Esta variável então assumirá apenas um dos três estados definidos. Mas quando o "loop" é executado pela primeira vez (e somente na primeira vez!!!) essa variável já indica o estado "SENHA_iniciar", o que garante que a Máquina tem um estado definido quando o Sistema inicia (ver área marcada em amarelo na figura).

      E o próximo estado da Máquina, dependerá do estado atual em que ela se encontra e do que acontece neste estado. Vejamos então o funcionamento desta Máquina, conforme descrito a partir do seu estado inicial, o que é descrito a seguir.

 

      No estado "SENHA_iniciar", que começa na linha 1021, as três variáveis "SENHA_IN_1", "SENHA_IN_2", e "SENHA_IN_3", que armazenam os valores que são mostrados nos três Displays, são zeradas, o que implica que veremos "000" nos Displays. Já a variável "DISPLAY_SENHA" é setada com valor "1", o que significa que o Display "1" está atualmente selecionado para a entrada de dígito da Senha. Ainda neste estado, os dois LEDs, "OK" e "ERRO" são desligados. Então a variável "estado_MAQ" é setada na linha 1032, para que a Máquina siga para o estado "SENHA_inserir".

 

      No estado "SENHA_inserir" (que começa na linha 1038) é onde ocorrem todas as operações relacionadas à entrada de Senha. Na linha 1039, o teste "if" verifica através da função "verifica_BOTAO_trigger_ONse o Botão "1" foi acionado, e caso isso tenha ocorrido, o "switch" na linha 1041 testa a variável "DISPLAY_SENHA" e conforme o valor desta (1, 2, ou 3) teremos que ou a variável "SENHA_IN_1", ou a "SENHA_IN_2", ou a "SENHA_IN_3", será decrementada, já que o Botão "1" tem esta funcionalidade.

      Mas se o Botão "1" não foi acionado, então o "else if" na linha 1061 verifica se o Botão "2" foi acionado, e de forma semelhante ao já descrito, uma das variáveis dos Displays será incrementada, já que o Botão "2" tem esta funcionalidade.

      Mas se o Botão "2" não foi acionado, então o "else if" na linha 1083 verifica se o Botão "3" foi acionado, e caso isto tenha ocorrido, então é selecionado o próximo Display através do incremento da variável "DISPLAY_SENHA". Mas aqui há mais coisas pra fazer, pois se "DISPLAY_SENHA" foi incrementada para além de "3" (ou seja, foi para "4"), significa que foi concluída a inserção dos 3 dígitos da Senha, o que é testado na linha 1087. Neste caso, deve ser verificado se a Senha inserida está correta, o que é testado na linha 1089 via função "verifica_SENHA" (que já analisamos), e se esta função retorna "true" então significa que a senha está correta, e neste caso o LED "OK" é ligado (na linha 1091). Mas se a função retornar "false", então a Senha inserida está incorreta, e o LED "ERRO" é ligado (na linha 1095). Independente se a Senha está correta ou não, como a entrada de Senha foi concluída, o Sistema deve aguardar antes de reiniciar todo o processo de entrada de Senha, e para isto na linha 1098 a variável "estado_MAQ" é setada para seguir para o estado "SENHA_aguardar".

 

      Uma vez no estado "SENHA_aguardar" (que começa na linha 1105), novamente é aguardado que o Botão "3" seja acionado. Porém quando isto for detectado, simplesmente a Máquina de Estados é direcionada para o estado "SENHA_iniciar", o que reinicia todo o processo de inserção de Senha.

 

      Como se vê, o processo é simples. Uma coisa importante a notar, é que a lógica mais complexa usada no processamento dos estados, é o "if / else" (o "switch" também é uma forma "sintética" de "if /else"). Isto é uma das coisas que garante a extrema confiabilidade do funcionamento da Máquina de Estados, implicando que a mesma seja praticamente livre de bugs (que são muito fáceis de ocorrer em lógicas malucas ou complexas), e ainda mantendo uma grande simplicidade.

 

 

 

      O código  e arquivos para Simulação estão neste link:  "Senha_7_seg_01.zip"

 

 

      Link para ver ou fazer download do vídeo da Simulação:  "video"

 

 

      A simulação ajuda a testar mais rapidamente o Sistema, permitindo se ver o funcionamento do Sistema sem precisar montar o circuito. Além disso possibilita que aqueles que não tem o Hardware em mãos, possam testar o Sistema e aprender com isso, fazendo suas experiências.

 

 

      Em caso de dúvidas, não deixe de perguntar.

 

      Espero ter ajudado.

 

      Abrçs,

      Elcids

RSS

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço