Willian Mateus Ferreira dos Santos, Autor em Blog Ryndack Componentes
Rua Jovelina Claudino Buhrer, 440 - São José dos Pinhais - PR (41) 3383-3034
Dicas

Para quem está acostumado a mexer com eletrônica, é muito comum usar motores DC em aplicações que exigem inverter seu sentido de rotação. Para isso, geralmente usamos módulos com ponte H como o módulo com o L298N. Entretanto, às vezes realizamos aplicações que não requerem muita sofisticação, e comprar esse tipo de módulo acaba sendo uma despesa indesejada. Dessa forma, nesse artigo vamos trazer um projeto de uma ponte H simples e de baixo custo, usando apenas alguns componentes baratos.

Como funcionam as pontes H

Antes de irmos para o nosso projeto de uma ponte H, precisamos entender o funcionamento desse tipo de dispositivo. Para explicar isso, podemos pensar na ponte como um conjunto de quatro chaves dispostas como na imagem abaixo.

Olhando para o esquema entendemos o seu nome “ponte H”, já que as chaves estão ligados formando um H mesmo. Com essa ligação acionamos as chaves sempre duas a duas, conseguindo assim inverter a polaridade da tensão, e da corrente na carga. Para ilustrar isso, pense que as chaves 1 e 4 estão fechadas. Então, a tensão de alimentação será encontrada na carga, além disso, a corrente elétrica pode fluir pelo caminho abaixo.

Ao fluir por esse caminho, a corrente passara pela carga indo da direita para a esquerda (sentido convencional). Entretanto se fecharmos as chaves 2 e 3 abrindo as chaves 1 e 4 abertas, na carga, a corrente passará a ter o sentido oposto como na imagem abaixo.



Assim, conseguimos controlar a direção da corrente elétrica na carga, apenas acionando as chaves duas a duas. Essa é a ideia por trás de uma ponte H.

Agora que sabemos como a ponte H funciona, podemos partir para a construção de uma. Para isso, no lugar das chaves, usaremos transistores, já que assim conseguimos fazer o controle de forma eletrônica, usando um Arduino.

Montando uma ponte H com transistores

Para montarmos a nossa ponte, usaremos 2 BC327 e 2 BC337, que estarão ligados nos lugares das chaves, usando resistores de 1k em suas bases para a polarização, como no esquemático abaixo.


Com os transistores ligados dessa forma já temos nossa ponte, entretanto, podemos fazer algumas melhorias, para que a nossa ponte possa de fato ser usada.

Melhorando nossa ponte H

A primeira melhoria que faremos é adicionar 4 diodos em paralelo com os transistores, e polarizados inversamente. Esses diodos, são chamados de diodos de roda livre, e eles são de grande importância para o funcionamento continuo da ponte. Eles atuam na proteção dos transistores contra tensões reversas, uma vez que as cargas que serão usadas nesse circuito são, na sua grande maioria, de natureza indutiva. No nosso caso, os diodos usados form o 1N4148, já que esses diodos apresentam um tempo de resposta pequeno, e a nossa ponte é pensada para pequenos motores.

Após essa etapa, conectamos as bases dos transistores 1 com o 2  e 3 com o 4. Essa conexão é feita com dois objetivos em mente. Em primeiro lugar buscamos evitar que os pares de transistores 1 e 2 ou 3 e 4 sejam ativados simultaneamente, já que isso seria um curto entre a fonte de tensão e o terra. O segundo objetivo é facilitar o controle, uma vez que nessa nova configuração só é necessário dois sinais lógicos, ao invés de 4, como era o caso. Assim, obtemos o resultado fica como na imagem abaixo.

É importante ressaltar que fazer essa conexão só é possível pois teremos transistores PNP com a base conectada à base de transistores NPN. Isso porque os transistores PNP conduzirão quando a tensão na base for menor que VDC-0,7V, enquanto os transistores NPN conduzirão com uma tensão de base de 0,7V. Dessa forma, ao aplicarmos uma tensão de 0V teremos o transistor PNP em condução e o NPN em bloqueio, enquanto teremos o contrário quando a tensão aplicada for igual a VDC.

Limitações da nossa ponte H

Afim de garantir o funcionamento dessa ponte H, precisamos levar em conta algumas limitações que ela possuí em seu funcionamento. A primeira diz respeito as correntes e tensão máximas, que são de 800mA e 45V respectivamente, uma vez que esses são os maiores valores suportados pelos transistores selecionados.
A segunda e mais problemática limitação diz respeito ao nível de tensão usado para o controle lógico. A tensão da entrada lógica deve ser 0V para o nível baixo e igual a tensão de alimentação da ponte para nível alto. Isso porque se a tensão na entrada lógica entre o intervalo de 0,7V até VDC-0,7V, teremos que os transistores NPN e PNP conduzirão ao mesmo tempo. Então, se quisermos controlar um motor com um Arduino por exemplo, será necessário usar uma fonte de alimentação e um motor de 5V.

Uma segunda topologia

Uma maneira de contornar isso é utilizar apenas transistores NPN, o que foi implementado por nós em uma segunda topologia, que esta representada abaixo.

Nessa nova topologia, contornamos o problema de tensão pois agora nenhum transistor precisa se encontrar em uma tensão próxima a VDC para se encontrar bloqueado. Com essa topologia, teremos como níveis lógicos 0V e qualquer valor maior que 0,7V como nível alto. Para o funcionamento, colocamos dois transistores a mais, que estão conectados às bases dos transistores da parte de cima da ponte. Esses transistores atuam como duas portas inversoras,que fazem com que os transistores da parte superior conduzam e bloqueiem em momentos diferentes dos transistores inferiores.

Entretanto, para essa montagem deve-se tomar alguns cuidados. O primeiro deles é a escolha dos resistores, que deve garantir que para a tensão selecionada o transistor, quando conduzindo, se encontre na região saturada. 

O segundo cuidado é que, diferente da montagem anterior, essa nova montagem não protege a ponte contra curtos caso as duas entradas se encontrem e nível alto, portando, essa combinação deve ser evitada.

Existem outras formas de construir pontes H com transistores, aqui apresentamos dois exemplos simples, que podem ser montados por menos de 5 reais, elas não são sofisticadas como as encontradas em módulos com circuitos integrados como o com o L298, mas já são o suficiente para usar pequenos motores. Você já conhecia essas pontes? Deixe no comentário. Lembrando sempre que todos esses componentes vocês podem encontrar no nosso site.

0

Conhecimento

A automatização da aquisição de medidas é uma estratégia fundamental para a pesquisa e desenvolvimento ou para a análise de qualidade industrial, em especial quando as medidas a serem realizadas são repetitivas ou complexas. A programação das medições permitem a obtenção de resultados de forma rápida e eficiente, possibilitando a realização de medidas contínuas, de longa duração ou com diferentes condições, evitando, por vezes, erros decorrentes da ação humana. Com processos de medidas automatizados, é possível coletar dados consistentes e de fácil análise em tempo real. Pensando nisso, escrevemos esse artigo, no qual explicamos como implementar SCPI em  python para que dessa forma, você possa automatizar processos de aquisição de medidas. 

O protocolo SCPI

Antes de realizar a programação efetivamente, é importante conhecermos as ferramentas que usaremos, isto é, o protocolo SCPI (Standard commands for programable instruments). Esse protocolo, como o nome já diz, consiste em comandos de texto padronizados que permitem o controle de dispositivos como fontes, multímetros, osciloscópios, entre outros, usando um computador conectado à mesma rede. Nessa padronização, temos palavras chaves que definem o que será feito pelo instrumento. Essas palavras são organizadas de forma hierárquica, então, os comandos usados dever seguir sempre uma mesma estrutura.

Um exemplo de comando em SCPI seria “MEASure:VOLTage:DC? 10, 1E-3”. Aqui, temos o comando principal “MEASure”, que indica o tipo de comando que queremos executar, que nesse caso é realizar uma medida. Os comandos seguintes são para selecionar dentro das medidas possíveis qual sera realizada, que nesse caso é VOLTage:DC, ou seja, tensão em corrente continua. Em seguida temos um ponto de interrogação (?) que indica que a operação a ser realizada é uma consulta, e portando, o dispositivo enviar o resultado para o computador. Finalmente enviamos os parâmetros “10, 1E-3”, que pré configuram o multímetro para tensões no range de 10V com uma resolução de 1mV.

No comando acima temos parte da palavra em maiúsculo e parte em minúsculo, mas isso é apenas para indicar qual parte do comando é obrigatório para o DM858 compreender, sendo que o comando será entendido se for enviado na sua forma completa, ou omitindo a parte minúscula. Além disso, o protocolo SCPI não é case sensitive, isto é, ele não diferencia letras maiúsculas das minúsculas. Assim, esse mesmo comando pode ser escrito como “meas:volt:dc? 10, 1e-3” ou como “MEAS:VOLT:DC? 10, 1E-3” ou qualquer combinação de maiúsculas e minúsculas, que ele funcionará normalmente. O manual completo de comandos SCPI do DM858 pode ser encontrado aqui.

Configurações inicias

Agora que já estamos familiarizados com o SCPI, podemos fazer umas configurações iniciais para a programação. O que usaremos para a interface de comunicação sera o VISA (Virtual instrument software architecture), que de forma simplificada é um conjunto de ferramentas que facilita a comunicação entre o computador e o instrumento de medição. Ele fornece uma camada de abstração, permitindo que o software controle o instrumento sem precisar lidar diretamente com os detalhes da interface física. O interessante do VISA é que ele não depende do tipo de interface realizada, e portanto pode ser usado quando o instrumento esta ligado por meio de Ethernet, USB, RS232, etc.

Então, para conseguirmos usar o VISA, precisamos instalar a biblioteca pyvisa, que pode ser obtida com o comando “pip install pyvisa-py”. Caso você não possua o pip em sua máquina, você pode encontrar o manual de instalação clicando aqui. Outras bibliotecas que usamos são as bibliotecas matplotlib para a geração de gráficos, que você pode instalar usando “pip install matplotlib”. A biblioteca pandas, para gestão arquivos do tipo .CSV, também foi usada, e você pode obtê-la usando “pip install pandas”.

Com essas instalações feitas, precisamos ainda conectar o multímetro DM858 a rede ethernet, e encontrar o endereço VISA, do multímetro. Isso é simples, para isso, basta realizar a conexão do cabo ethernet na parte de trás do multímetro e no roteador, que a conexão está pronta. Agora, no multímetro, tocamos no “R”, em seguida abrimos o menu “Sistema”, e selecionamos a aba “Rede”, então encontraremos o endereço VISA do seu instrumento. Anotamos esse endereço pois usaremos ele mais adiante.

Programando

Finalmente vamos para o código usado no nosso projeto, que começa importando as bibliotecas que instalamos anteriormente, além da biblioteca “time”, que foi usada para realizar delays e gravar o tempo entre as medidas.

import pyvisa as visa
import pandas as pd
import matplotlib.pyplot as plt
import time

# Define quantidade de medidas e intervalo entre elas
num_medidas = 30
intervalo = 5
# Cria as listas para armazenar as medidas e o intervalo de tempo
momento = [ ]
temperatura = [ ]

Em seguida, definimos o número de medidas e o intervalo entre as medidas, iniciando as variáveis ”momento” e  “temperatura” logo após. Essas variáveis serão usadas para gravar o instante em que a medida foi realizada e a temperatura encontrada respectivamente.

O próximo passo foi criar uma instância para o gerenciamento da comunicação entre o instrumento e o computador, abrindo o “multímetro”, usando o método “open_resource(“Endereço visa”)”. Para este método que enviamos o endereço visa anotado anteriormente. Por fim configuramos a terminação da comunicação, tanto para leitura, quanto para a escrita.

# Configura a comunicacao com o multimetro
rm = visa.ResourceManager()
rm.list_resources()
multimetro = rm.open_resource("TCPIP::192.168.1.93::INSTR")
multimetro.read_termination = '\n'
multimetro.write_termination = '\n'

Na etapa seguinte, realizamos as medidas. Aqui configuramos a unidade de temperatura do multímetro para graus celsius e realizamos as medidas em um loop, que no nosso caso é repetido 20 vezes, com uma vez a cada 5 segundos. Essas quantidades podem ser ajustadas, alterando-se os valores das grandezas “num_medidas” e “intervalo”. Isso ficou como abaixo.

# Realiza as medidas
start_time = time.time()
multimetro.write("UNIT:TEMP C")
for i in range(num_medidas):
    momento.append(time.time() - start_time)
    temperatura.append(float(multimetro.query("MEAS:TEMP? TC, K")))
    time.sleep(intervalo)
rm.close()

Dentro do loop acima, adicionamos o instante e a leitura no final das listas “momento” e “temperatura”,  em seguida, adicionamos uma pausa, antes de realizar a próxima medida. Aqui é importante notar os métodos .write e .query que foram usados. Começando pelo comando .write, temos que ele envia um comando em SCPI para o multímetro, sendo que ele deve ser usado para enviar comandos de configuração. Enquanto isso, o método .query envia comandos e realiza a leitura da resposta do multímetro, por isso usamos ele para enviar o comando “MEAS:TEMP? TC, K”, que realiza a leitura de um termopar tipo k. 

Finalmente vamos para as saídas do nosso programa, que são um arquivo .CSV, resultado do pandas, e um gráfico, advindo da biblioteca matplotlib.

# Salva as medidas em um arquivo CSV
log = pd.DataFrame(data={"momento": momento, "temperatura": temperatura})
log.to_csv("leituras.csv", index=False, sep=",",decimal=",")

# Plota o grafico
fig, ax = plt.subplots(figsize=(10, 5))
ax.set_title("Temperatura x Tempo")
ax.set_xlabel("Tempo")
ax.set_ylabel("Temperatura")
ax.plot(momento, temperatura, color = "red", marker = "o", markerfacecolor = "white", markersize = 5)
ax.xaxis.set_major_locator(plt.MaxNLocator(nbins=7, prune='both'))
plt.tight_layout()
plt.show()

Testando

Agora que já fizemos a nossa programação, podemos ver o nosso código funcionando na prática. Para isso, fizemos uma montagem bastante simples, apenas para ilustrar o funcionamento do código. Essa montagem consiste apenas ao termopar preso junto a ponta de um ferro de solda com o auxilio de uma fita kapton, assim, conseguimos medir a temperatura desse equipamento. Usando essa montagem fizemos o gráfico abaixo.

Nesse gráfico, ligamos o ferro de solda ao mesmo tempo em que executamos o programa escrito. As marcações presentes neste gráfico são as medidas que realmente realizamos, isto é, os valores das variáveis momento, representados na horizontal, e temperatura, na vertical. O formato e a cor desses marcadores, além da cor da linha foram definidos na linha “ax.plot(momento, temperatura, color = “red”, marker = “o”, markerfacecolor = “white”, markersize = 5)”.  Os valores plotados nesse gráfico estão no arquivo .CSV gerados pela biblioteca “pandas”, assim você pode usa-lo em um outro software de sua preferencia. Para acessar o arquivo .CSV, precisamos tomar cuidado com os separadores de casas decimais e de células do arquivo.

Neste artigo discutimos um pouco sobre como usar o protocolo SCPI para controlar instrumentos de bancada, realizando uma pequena automação com o Python. Essa automação é simples, mas ela já serve para demonstrar um caminho para a automação de medidas mais sofisticadas.

Se você esta buscando instrumentos que podem ser usados para as suas medições, a Ryndack Componentes, distribuidora oficial da Rigol, conta com diversos equipamentos como multímetros, osciloscópios e fontes de alimentação.

0

Conhecimento

Se você gosta de mexer com eletrônica, você já deve ter tido a ideia de fazer uma placa de circuito impresso, afinal, ver as trilhas desenhadas em uma placa é algo fascinante. Além disso, uma placa de circuito impresso permite a construção de projetos compactos e duráveis, já que, a menos que exista problemas na corrosão, a chance de você ter mal contatos é bem menor que no caso dos jumpers de protoboards convencionais.

Pensando nisso, escrevemos este post, no qual ensinamos como você pode construir um placa de circuito impresso de forma bem manual, e econômica. Neste artigo usamos poucos matriais, sendo eles uma placa virgem, um cortador de placas, um perfurador de placas, uma caneta permanente e percloreto de ferro.

Esquemático e Layout

A primeira coisa que precisamos para nossa placa é do seu layout. O layout é uma prévia da placa, isto é, nele temos a posição dos componentes e das trilhas. Como o foco deste artigo é montar a placa, vamos apenas apresentar o layout e o esquemático usados, não nos atendo a criação destes. Começando pelo esquemático que usamos, ele está na imagem abaixo.

Você talvez reconheça esse projeto, já que ele é muito semelhante ao do nosso artigo no qual montamos um vai e vem de leds, mas caso não conheça, você pode acessa-lo aqui. Usamos este esquemático para montar o layout abaixo.

Com esse layout, podemos começar o processo de corrosão da placa.

Cortando a placa para a corrosão

Como já mencionado, para montarmos nossa placa precisamos de alguns itens. Você pode adquirir eles separadamente, mas se desejar, eles podem ser encontrados no kit disponível aqui.

Para corroer a nossa placa, começamos cortando ela, para isso, usamos o cortador presente no kit. Primeiro posicionamos a placa no meio da régua de alumínio, medindo o tamanho que a placa devera ter, no nosso caso a placa devera medir 52mm x 58mm. Em seguida, usamos o riscador para marcar bem, dos dois lados da placa, o lugar onde ela deverá ser cortada. A placa na verdade será quebrada, então os riscos devem ser bem feitos, sendo que o quanto mais profundo melhor.

Depois de riscar a placa, apoiando ela em uma superfície reta a quebramos, sendo que, caso necessário, as rebarbas podem ser removidas com uma lixa fina. Importante tomar sempre cuidado para não danificar a parte de cobre, já que, apesar de boa parte dele ser corroída, se alguma trilha estiver rompida o circuito não funcionara.

Furando a placa

Com a placa do tamanho certo, prosseguimos furando a placa. Para isto, imprimimos o layout em escala, em seguida, prendemos ele sobre a placa. Importante lembrar que o layout deve ser impresso de forma espelhada, já que as trilhas e os componentes estão em camadas diferentes. Além disso, neste projeto usamos uma placa de face simples, então o layout impresso deve ser fixado sobre a pate cobreada.Para fixarmos o layout usamos fita adesiva, sendo que o resultado ficou como abaixo.

Agora podemos começar a furar a placa com o perfurador. Nesta etapa precisamos tomar o cuidado de perfurar nas posições corretas dos footprints presentes no layout, isto é, furar os lugares corretos para que os componentes possam ser posicionados.

Desenhando a placa para a corrosão

A próxima etapa do nosso processo de corrosão de placa é desenhar as trilhas na placa. Esta etapa é a mais complicada, já que ela exige um pouco de destreza. O que temos que fazer aqui é cobrir as trilhas, para que elas não sejam corroídas com o restante do cobre.

Antes de desenhar as trilhas, começamos limpando bem o cobre, para garantir que o tinta se fixe ao cobre. Para essa limpeza usamos um papel toalha e álcool isopropílico, mas ela pode ser feita com palha de aço, ou outro produto de limpeza que não agrida o cobre.

A seguir, começamos a desenhar a placa. Aqui existem outros métodos, como o da transfusão térmica, mas nós vamos fazer o que provavelmente é o mais simples. O que faremos sera simplesmente desenhar as trilhas da placa com um marcador permanente. Aqui deve ser seguido o desenho espelhado da placa, do mesmo modo que foi feito durante a furação. Além disso, é bom deixar separado álcool isopropílico e um cotonete, para caso o desenho da trilha saia errado.

Aqui vale lembrar que todo o cobre exposto será corroído, assim, se o preenchimento da trilha não for bem feito, teremos que elas podem ser corroídas, rompendo a trilha. Da mesma forma, se duas trilhas se aproximarem de mais a ponto de se tocarem, após a corrosão elas estarão ligadas, por isso devemos cuidar para desenhar as trilhas da forma mais espaçada possível.

Preparo da solução e corrosão da placa.

Agora que já preparamos a placa, vamos par a corrosão dela. Durante esta etapa, temos algumas recomendações. Começando com o uso de luvas, já que o percloreto é uma substância corrosiva, que pode causar danos a pele. Por esse mesmo motivo, a corrosão deve ser feita em lugares arejados, evitando o uso de utensílios de metal, que podem ser corroídos. Além disso, tenha em mente que o percloreto de ferro é uma substância que pode causar manchas permanentes, então seu manuseio deve ser feito com cuidado. Dito isto, podemos começar a preparar a solução para a corrosão.

Para o preparo da solução, seguiremos as instruções do fabricante. No caso do kit que estamos usando, precisamos diluir o conteúdo da embalagem de percloreto (pó preto) em água. Assim, começamos separando 500ml de água em um pote de plastico ou vidro, e em seguida despejamos o conteúdo da embalagem, com cuidado para não derrubar, misturando com uma colher de madeira ou plástico. Feito isto, você vai notar que a solução de percloreto se aquece e sua cor ficará marrom. Quando tivermos o percloreto totalmente diluído, podemos mergulhar a placa para iniciar o processo de corrosão.

A corrosão em si é bem simples, basta mergulhar a placa e deixar o percloreto de ferro agir. Para acelerar o processo, podemos movimentar a placa de tempos em tempos, evitando esfregar a superfície cobreada. Além disso, se você quiser, você pode furar algum lugar que não passe nenhuma trilha e amarrar um fitilho, ou um pedaço de fio esmaltado para facilitar o manuseio. Em pouco tempo você vai notar o cobre sendo consumido, e quando só as trilhas estiverem na placa terminamos com a corrosão.

Limpeza e reparo

Com a corrosão da placa pronta, podemos transferir o percloreto para um pote ou garrafa com tampa, e guarda-lo em um lugar seguro, para posterior reutilização. Enquanto a placa precisa ser lavada. Aqui podemos usar água mesmo, mas é importante remover qualquer resto de percloreto de ferro que tenha ficado na placa. Se essa limpeza não for feita o cobre continuará sendo corroído, e como consequência podemos ter rompimento de trilhas. Após a lavagem, removemos a tinta do marcador permanente, esfregando um papel toalha com álcool isopropílico. Nesse momento podemos ver como as trilhas ficaram.

Com as trilhas expostas, podemos testar e corrigir problemas gerados na etapa de corrosão. Uma forma de testar as trilhas consiste em realizar testes de continuidade entre elas, assim, conseguimos não apenas verificar se uma trilha foi rompida, como também encontrar trilhas distintas que estejam se tocando.

Durante estes testes, é normal encontramos algumas trilhas defeituosas, mas isso não significa que o processo deu errado. No caso em que existem trilhas com defeitos, podemos conserta-las manualmente. Começando pelos trechos que não foram corroídos, podemos raspa-los usando o riscador de placas. Por outro lado, as trilhas que foram rompidas podem ser reparadas durante a soldagem dos componentes, apenas completando os pontos rompidos com estanho.

Finalizando

O ultimo passo é montar a placa, isto é, soldar os componentes nela. Aqui também aproveitamos para reparar as trilhas que estão rompidas. Além disso, indicamos o uso de um estanho e de uma ponta de ferro de solda finos, para garantir maior precisão na soldagem.

Nessa parte também precisamos tomar cuidado para não danificar as trilhas do circuito, em especial em torno da parte cobreada.

Com isso terminamos nossa placa de circuito impresso. O resultado ficou como no vídeo abaixo.

Link do vídeo

Esse post foi para ensinar um método bem simples de se fazer a corrosão de placas de circuito impresso. Ele usou poucos materiais, e foi um método bastante manual. Vale lembrar que o kit de fabricação caseira de placas pode ser encontrado no site da Ryndack Componentes, clicando aqui. Além disso, caso você tenha apenas se interessado pelo projeto executado, você pode compra-lo aqui.

0

Projetos


Um projeto bastante interessante para quem está começando a mexer com eletrônica é o vai e vem de leds, afinal, apesar desse projeto ser simples, de fácil montagem e barato, eles de gerar efeitos visuais bonitos, capazes de serem empregados em decorações, brinquedos, vitrines, etc. Nesse post, vamos usar o contador/divisor binário CD4017 e um LM555 mostrando como cada um deles funciona, assim você poderá usa-los em outros projetos, de acordo com a sua necessidade.

Para simplificar a compreensão do nosso circuito, começaremos explicando o funcionamento do LM555 e do CD4017. Em seguida realizaremos nossa montagem, que consiste simplesmente em usar esses dois CIs, um para gerar o clock, e o outro para acionar os leds.

LM555, o clock do nosso vai e vem de leds

O primeiro circuito integrado do qual vamos falar nesse post é o famoso LM555. Esse circuito integrado é um timer de uso geral, e no nosso circuito, ele será o responsável por fornecer o sinal de clock, que por sua vez será usado pelo CD4017. Para entende bem seu funcionamento, pensamos na sua estrutura interna, que pode ser representada como abaixo:

Nesse esquema vemos um divisor resistivo, que divide a tensão de alimentação em três partes iguais (\frac{VCC}{3}, \frac{2*VCC}{3} e VCC). Em seguida, temos dois comparadores, o primeiro, compara o pino “TR” com \frac{VCC}{3}, resultando em um nível baixo, quando "TR">\frac{VCC}{3}. Esse comparador é ligado no pino de “SET” de um latch RS, dessa forma, a saída será um nível alto quando "TR"<\frac{VCC}{3}, e dependera do pino de reset caso contrário

O segundo comparador compara o pino “THR” com \frac{2*VCC}{3}, tendo saída alta quando "THR">\frac{2*VCC}{3}. Essa saída esta ligado no pino de reset do latch, então, quando "THR">\frac{2*VCC}{3} a saída do latch será nível baixo. Esse latch também conta com um pino de reset externo, que reinicia as saídas do latch por meio de uma fonte externa.

Na saída negada desse latch temos um transistor conectado ao pino “dis”, cuja função ficará clara mais a frente.

Modos de operação monoestável

O LM555 pode operar em dois modos distintos, o monostável e o astável. Começando pelo monostável, nesse modo teremos um pulso de saída em resposta a um sinal de gatilho, com a duração do pulso de saída ajustável. A sua montagem fica como abaixo.

Nessa montagem, quando o pino “TR” recebe um sinal de nível lógico baixo, o latch será setado, e a saída negada irá para baixo, o que faz com que o transistor do pino “DIS” se encontre bloqueado. Então, o capacitor estará carregando, até que ele atinja 2*VCC/3, quando o latch sera resetado, e o transistor passará a se encontrar fechado, descarregando o transistor, até que um novo pulso em “TR” ocorra. Para calcular o tempo de duração do pulso de saída temos a fórmula:

(1)   \begin{equation*}t_{pulso}=1,1*R_{cap} * C\end{equation*}

Modo de operação astável

Vamos agora para o segundo modo de operação, isto é, para o modo astável. Esse modo é o que utilizaremos no nosso projeto, já que ele apresenta na saída uma onda quadrada, cujo os tempos em alta, em baixa e a frequência são ajustados por componentes externos. Para configurar o LM555 nesse modo, precisamos monta-lo como abaixo:

O modo astável usa um capacitor da mesma forma que o modo monoestável usava. A diferença é que o modo astável usa a descarga do capacitor para espaçar os pulsos no pino “TR”. Para mostrar isso, usando R1 = 1k, R2 = 3,9k e C = 2,2uF medimos as formas de onda da saída e da tensão no capacitor, que ficaram como abaixo:

Aqui temos que a tensão de alimentação era de 5V, então, quando a tensão do capacitor (em azul) atinge aproximadamente 1,67V (frac{Vcc}{3}) a saída vai para nível alto. Em seguida,o capacitor carrega até atingir 3,33V (frac{2*Vcc}{3}), quando a saída passa para baixo. Com isso, temos que o tempo em alta é igual ao tempo de carga do capacitor. Da mesma forma, o tempo de descarga do capacitor equivale ao tempo em baixa do sinal de saída, sendo que esses tempos podem ser obtidos com:

(2)   \begin{equation*}t_{alto}=0,693*C*(R_1+R_2)\end{equation*}


(3)   \begin{equation*}t_{baixo}=0,693*C*R_2\end{equation*}

Agora que já conhecemos o LM555, vamos falar sobre o CD4017.

CD4017 o driver do vai e vem de leds

O CI 4017 é um contador/divisor binário o que ele faz é acionar uma saída diferente para cada pulso de clock. O seu circuito funciona como uma sequência de flip-flops D, ligados em sequência, isto é, a saída do anterior conectado a entrada do próximo. A imagem abaixo mostra isso:

Note que a ultima saída negada é a que esta sendo ligada no primeiro flip-flop, assim, todas as saídas passaram a alta, uma a uma, e depois serão colocadas em baixa da mesma forma. Para que cada uma das saídas sejam acionadas uma de cada vez, um circuito de lógica combinacional é aplicado. Com esse circuito lógico, conseguimos converter as 5 saídas em 10, que é a quantidade de saídas do CD4017.

Finalmente, podemos falar sobre o circuito montado para o vai e vem de leds.

O vai e vem de leds

Para montar o circuito usamos, além do LM555 e do CD4017, 6 leds, 6 resistores de 330R, 8 diodos 1N4148, 1 resistor de 330k, 1 resistor de 1k e 1 capacitor de 2,2uF, que foram ligados como no esquema abaixo.

Aqui configuramos o LM555 no modo astável para gerar um clock de 1Hz, com duty cicle de 50%, conectando a saída na entrada do CD4017. O passo dado em seguida por conectar os leds, sendo que foram necessários diodos para 4 deles. Usamos os diodos para ligar duas saídas do CD4017 a um mesmo led, já que assim conseguimos gerar um efeito de “vai e vem”. Lembrando que esses diodos são necessários para não queimar o CD4017, uma vez que, se elas fossem ligadas diretamente teríamos um curto circuito. Os resistores de 330R usados servem para evitar corrente muito grandes nos leds, garantindo que eles funcionem, assim, esses resistores podem ser substituídos caso você use tensões maiores (lembrando sempre de respeitar as tensões de operação dos CIs usados). Testamos o nosso circuito, e mostramos ele funcionando no vídeo abaixo.

Nesse artigo trazemos um projeto simples de um vai e vem de leds, e aproveitamos para discutir um pouco sobre os circuitos integrados CD4017 e LM555, que são CIs bastante versáteis na eletrônica. Já conhecia eles? Conte pra gente! Lembrando sempre que os componentes usados nesse projeto, e muitos outros podem ser encontrados na Ryndack Componentes clicando aqui.

0

Conhecimento


Medir a intensidade de campo magnético de objetos pode ser um maneira bastante eficiente de extrair informações uteis dele. Afinal, avaliando o campo magnético podemos definir grandezas como posição, velocidade, correntes, entre outras. Por isso, saber como fazer isso é muito importante para quem gosta de eletrônica. Pensando nisso, trazemos este artigo apresentando para vocês o SS49E, um sensor baseado no efeito Hall, que é barato, mas extremamente versátil e capaz de realizar ótimas medidas de campo magnético.

Conhecendo o sensor de efeito Hall

Como já mencionado, esse sensor tem como base o efeito Hall, um fenômeno curioso do electromagnetismo. De maneira simplificada, esse fenômeno se trata do surgimento de uma tensão elétrica, quando uma corrente passa por uma região de campo magnético presente. A figura abaixo ilustra isso.

Nessa imagem, temos o campo magnético e a corrente elétrica passando por uma região com um campo magnético, e perpendicular a ambos temos a tensão Hall, representada por \ V_H. É importante ressaltar que a tensão Hall sempre é perpendicular ao campo magnético e a corrente simultaneamente, isso é, ele é perpendicular ao plano que os contém.

Outra característica da tensão Hall é que ela é proporcional a densidade do fluxo magnético e a corrente elétrica. Então, se desejamos medir o campo magnético devemos manter a corrente constante e medir a tensão Hall gerada no material condutor.
Agora que já apresentamos um pouco sobre o efeito Hall, podemos ir para o seu uso na prática.

Montando o circuito

O SS49E, sensor usado nesse artigo possui aplicação bem simples, se tratando de um sensor com apenas três terminais, sendo dois deles usados para a alimentação e o terceiro para saída. Assim, montamos um pequeno circuito usando um Arduíno UNO. Além disso usamos alguns jumpers e o protoboard para a conexão, o esquema abaixo mostra como ele foi montado.

Nessa montagem, usamos os pinos de 5V e GND do Arduino para alimentar o sensor, ligando-os aos pinos 1 e 2 respectivamente. Já o terceiro pino do sensor foi ligado diretamente à entrada analógica A0 do Arduino. Aqui é importante usar uma entrada analógica, pois a saída do sensor varia continuamente de 0V à 5V, dependendo da intensidade do campo externo. A relação entre a tensão de saída e o campo magnético é explicado um pouco mais adiante.

Programando

Com o circuito montado, podemos escrever o código que será executado no Arduino. Como esse código serve apenas para ilustrar o uso do SS49E mantivemos ele simples. Com ele fazemos a leitura da entrada analógica, convertemos o valor lido a um valor de campo e finalmente exibimos os valores envolvidos no monitor serial. Esse código está abaixo, mas como todos os códigos de nossos projetos, você pode encontra-lo em nosso github.

// Pino do sensor como o A0
const int pinoSensor = A0;

// Valor recebido pelo conversor A/D
int Valor_medido = 0;
// Valor_medido convertido para tensao
float Tensao_lida = 0;
// Valor de campo calculado
float Campo_calculado = 0;

void setup() {
  //inicia comunicação serial
  Serial.begin(9600);
}

void loop() {
  // Leitura do valor de 0 a 1023 do conversor A/D:
  Valor_medido = analogRead(pinoSensor);

  // Conversão do valor lido para a tensao de saida do sensor:
  Tensao_lida = 5.0*Valor_medido/1023;

  // Calculo do valor de tensão do campo 
  Campo_calculado = (Tensao_lida-2.5)/0.019;

  // Envia os resultados para a porta serial:
  Serial.print("Valor Medido = ");
  Serial.print(Valor_medido);
  Serial.print("\t Tensão_sensor = ");
  Serial.print(Tensao_lida);
  Serial.print("V");
  Serial.print("\t Campo = ");
  Serial.print(Campo_calculado);
  Serial.print("mT");
  Serial.print("\n");
  delay(500);
}

Começamos o código definindo a entrada analógica como sendo a A0 e declarando as 3 variáveis principais. A primeira é a “Valor_medido”, onde armazenamos o valor lido pelo conversor analógico digital do Arduino. Nessa variável constará sempre um valor de 0 até 1023, uma vez que o conversor A/D do Arduino possui 10 bits.

A segunda variável é a “Tensao_medida”, onde convertemos o valor obtido pelo conversor A/D em um valor de tensão. Para isso, multiplicamos o valor medido pela tensão mais alta que pode ser lida pelo Arduino (5V) e dividimos pelo nível mais alto do conversor (1023), ou seja, fazemos.

(1)   \begin{equation*}Tensao \_ lida = \frac{5.0*Valor\_medido}{1023}\end{equation*}


Finalmente, usamos a variável  “Campo_calculado” para armazenar o valor do campo magnético. Para isso, usamos os dados do datasheet do SS49E para determinar a equação da reta \ V_H X B. Nele observa-se que a sensibilidade do sensor é de aproximadamente 1,9mV/Gs, que convertendo equivale a 0,019V/mT. Além disso, temos que para um campo de 0mT, a tensão sera de 2,5V, assim, montamos a equação:

(2)   \begin{equation*}campo = \frac{Tensão - 2,5}{0,019}\end{equation*}

O que o loop em execução faz então é realizar a leitura do conversor A/D, determinar a tensão do sensor, calcular o valor do campo e finalmente enviar todos os dados coletados para o monitor serial.

Testando o circuito

O vídeo abaixo mostra o circuito funcionando.
https://youtube.com/shorts/t-J75gkJPYY?feature=share

Nele vemos que na ausência de um campo magnético a leitura tida é de aproximadamente 2,5V. Sendo que esse valor aumenta quando aproximamos um dos pólos do ímã. Ao aproximar o outro polo,  a tensão de saída do sensor reduzirá, permitindo assim realizar a distinção entre o pólo norte e sul do ímã.

Outro ponto importante de notarmos é que os valores de leitura estão sempre entre -100mT a 100mT, ou de 0,86V a 4,21V. Isso acontece porque valores de campo maiores que estes saturam o sensor, não podendo ser lidos adequadamente. Além disso, você pode aumentar a precisão do sensor obtendo a sensibilidade experimentalmente, medindo a tensão de saída para diferentes campos conhecidos.

Lendo o sensor com o DM858

Finalmente, usamos o multímetro digital DM858 da Rigol para realizar essas medidas, já que esse multímetro consegue realizar a leitura de diversos tipos de sensores de maneira prática e rápida, apenas inserindo um conjunto de valores conhecidos.

Afim disso, mantemos a alimentação do sensor com o Arduino, e conectamos o GND e a saída ao pinos do multímetro, em seguida, na tela tocamos no ícone da função atual e selecionamos “sensor”, então vamos para a aba “measure” e selecionamos “customize sensor”. Após isso tocamos em “+” e inserimos os dados conhecidos. Aqui colocamos os pares “2,5V e 0mT” e “5V e 131,579mT”, que satisfazem a equação 2, apesar do segundo ponto não ser possível, já que o sensor se encontraria saturado. Por fim em “sensor unit” escolhemos “customize” e digitamos “mT” no campo ao lado.

Com o multímetro configurado, a tela encontrada sera como abaixo:

Após realizarmos algumas medidas com o multímetro, vemos que os valores se aproximam bastante dos medidos com o Arduino, o que nos leva a acreditar que o código que fizemos está funcionando adequadamente.

Aplicações do sensor Hall

Agora que sabemos que o sensor está funcionando, usamos ele para traçar o gráfico do campo magnético x distância. Para isso, deixamos o sensor parado, e com o auxílio de uma régua, aproximamos um imã à ele em passos de 1 centímetro, obtendo como resultado o gráfico abaixo.

Esse interessante gráfico mostra como o campo magnético decai a medida em que a distância aumenta, além disso, ele mostra que esse tipo de sensor pode ser usado para se medir pequenas distâncias, apesar da baixa linearidade. 

Outra aplicação na qual esse tipo de sensor é aplicada é na medição de velocidade angular em motores. Para isso, basta ter um pequeno imã no eixo do motor, que sempre que ele passa pelo sensor um pulso de tensão será emitido, assim, a velocidade pode ser obtida contando esses pulso.

Já conhecia o sensor SS49E? Conte para gente nos comentários o que você achou deste post. Lembrando que esse sensor e muitos outros você pode encontrar na Ryndack Componentes clicando aqui.

0

Conhecimento

Os LEDs são um dos componentes mais utilizados na eletrônica, estando presentes em quase todos os equipamentos eletrônicos que usamos no dia a dia. Por serem bonitos, baratos e de fácil uso, é difícil encontrar eletrônicos que não possuam pelo menos um LED em sua montagem. Porém, apesar de serem bastante versáteis, pouca gente sabe como eles funcionam. E é por isso que trazemos esse artigo, explicando detalhadamente o funcionamento dos LEDs.

O LED como um diodo

Antes de mais nada, é preciso entender que os LEDs são diodos emissores de luz, ou seja, seu funcionamento é o mesmo que um diodo comum. Tanto os LEDs quanto os diodos comuns são junções P-N, e podem conduzir ou bloquear a corrente elétrica, dependendo da sua polarização. 

Entretanto, existem algumas diferenças entre a operação dos LEDs e dos diodos comuns. Começando pela polarização inversa, quando ambos bloqueiam corrente. A tensão máxima que os LEDs suportam costuma ser muito baixa, não excedendo 5V, na maioria das vezes. Por outro lado, em diodos comuns é normal esse valor chegar ou até exceder 1000V. A outra diferença está na tensão necessária para condução, quando o diodo está diretamente polarizado. Em diodos comuns, essa tensão costuma girar em torno de 0,7V, entretanto para os LEDs, esse valor geralmente esta entre 2V a 3,5V, dependendo da cor.

Essas diferenças aparecem pois os diodos comuns foram construídos com propósitos diferentes. Já que enquanto os diodos foram feitos para explorar a capacidade de condução e bloqueio, nos LEDs a propriedade de interesse é a eletroluminescência. Apesar dessas diferenças, é importante entender que o led continua sendo um diodo, e por isso, discutiremos um pouco sobre os diodos a seguir.

Como os diodos são construídos

Para entender bem o diodo, é preciso entender sua construção. Eles são formados por um material semicondutor puro conhecido como intrínseco, que recebe impurezas selecionadas. Essas impurezas tem o propósito de garantir ao semicondutor as propriedades físicas desejadas. A essas impurezas chamamos de material dopante, e ao semicondutor após a dopagem de extrínseco. Os semicondutores extrínsecos podem ser de dois tipos:

O primeiro tipo é formado quando o material dopante possui um elétron a mais que o semicondutor na sua camada de valência, assim quando o dopante e o semicondutor são ligados, um elétron fica disponível como elétron livre, servindo como um portador de carga. Esse tipo de semicondutor extrínseco é chamado de tipo N. 
 
O segundo tipo é o P, e ele se forma quando o material dopante apresenta um elétron de valência a menos que o semicondutor intrínseco. Nessa situação, na ligação entre o dopante e o semicondutor falta um elétron, assim surge uma lacuna (ou buraco, do inglês hole) como portador de carga.

Um diodo é a junção de um semicondutor P com um semicondutor N, e ele é formado quando se deposita dopantes do tipo N sobre um semicondutor extrínseco do tipo P (ou vice e versa), o que gera uma região com dopagem N e uma região com dopagem P juntas em um mesmo cristal semicondutor. Abaixo temos uma representação de uma junção P-N, nela temos lacunas no semicondutor P e elétrons livres no semicondutor N.

Como funciona a junção P-N

Quando a junção P-N é formada, temos na região da junção o deslocamento de elétrons do lado N para o lado P. Esse deslocamento deixa as proximidades da junção no lado P com carga negativa. Após esse deslocamento de elétrons, lado N da junção fica com carga positiva, como mostra a figura abaixo.


Dessa forma,  na região próxima da junção surge um campo elétrico que aponta de N para P, que constitui uma barreira de potencial elétrico. Essa região recebe o nome de região de depleção, e é essa região responsável tanto pelo bloqueio na polarização inversa, quanto pela tensão de polarização direta.

Quando submetemos ao diodo a uma tensão elétrica temos duas situações:
A primeira é quando temos o polo positivo da fonte ligada a região N e o negativo a região P. Nesse caso temos um campo externo de mesma direção ao campo da região de depleção. Nessa configuração, os portadores de carga do semicondutor são atraídos em direção a fonte e, consequentemente, a região de depleção aumenta, não permitindo que a corrente da fonte flua. Nesse caso, dizemos que o diodo esta inversamente polarizado.

A outra situação é quando o polo negativo esta ligado ao semicondutor N e o positivo ao semicondutor P. Assim os campos externo e da região de depleção estão em sentidos opostos, e se o campo elétrico é o suficientemente grande, portadores de carga conseguem vencer a barreira de potencial da camada de depleção. Quando isso ocorre, elétrons e lacunas se recombinam, o que por sua vez libera espaço para que a corrente elétrica da fonte flua livremente.

Como os LEDs emitem luz

Enquanto o diodo está em condução, os elétrons e as lacunas estarão se recombinando, entretanto, para que isso ocorra é necessário que os elétrons saltem da camada de condução para a banda de valência. Para realizar este salto os elétrons precisam perder parte de sua energia. É nesse momento que um LED se diferencia de um diodo convencional. Em um diodo comum a energia é liberada para o cristalino do semicondutor, na forma de calor, ou seja, sem emitir radiação eletromagnética. Porém, em um LED essa energia é liberada na forma de um fóton que é radiado para o ambiente.

A cor da luz emitida pelo LED depende da distância energética entre as bandas de condução e valência do material, isto é, depende da energia perdida pelo elétron. Essa distância varia de acordo com o material ao qual o semicondutor é formado. Alguns exemplos de materiais usados na fabricação de LEDs são o arseneto de gálio, que produzem LEDs infravermelhos e fosfeto de gálio, para LEDs vermelhos, amarelos e verdes.

Tipos de Leds

Atualmente existem diversos tipos de LEDs como os SMD’s, amplamente utilizados em placas de circuito impresso devido ao seu tamanho compacto. Eles podem ser de diversas cores, sendo um modelo bastante comum os RGB endereçáveis, com eles é possível formar uma diversa gama de cores com facilidade e rapidez.

Outro LED comumente encontrado é o infravermelho, usado na maior parte de controles remotos, além de sensores reflexivos como o TCRT5000. Esses LEDs não emitem luz na faixa visível, mas sim na faixa infravermelha, sendo assim usado para a transmissão de sinais em curtas distâncias e de forma direcionada.

A Ryndack Componentes possui uma grande variedade de LEDs em seu catálogo. Por la você também consegue encontrar diversos outros itens para te ajudar em seus projetos. Tá afim de conhecer nossos produtos, então de uma olhadinha no nosso site.

0

Projetos

Um dos jeitos mais interessantes de se interagir com equipamentos eletrônicos é por meio de um display LCD. Eles deixam a interface entre homem e máquina simples e intuitiva, afinal, é bem bacana poder visualizar as informações do seu display. Porém controla-los com um Arduino pode parecer desafiador, já que isso exige interfacear o microcontrolador e o display. Mas essa tarefa não é tão difícil assim, e é isso que mostraremos neste post.

Conhecendo os displays

Antes de começarmos a programar, vamos ver um pouco como os displays funcionam. O display que vamos usar para esse artigo é um 16×2, que são bastante comuns em equipamentos eletrônicos. Como o nome diz, eles possuem 16 colunas e 2 linhas, ou seja, conseguimos exibir neles até 32 caracteres. Cada caracter é formado por um conjunto de pequenos quadradinhos que podem ter sua cor alterada. Esses quadradinhos estão dispostos em 8 linhas com 5 quadradinhos cada, como na imagem abaixo.


Então, quando queremos escrever algo no display, ligamos ou desligamos os quadrados, fazendo um “pixel art” de ligado e desligado. Assim, se quisermos fazer um “R” por exemplo, precisamos ligar os quadradinhos adequados, como abaixo. 

Como controlar 40 pontos (8×5) por espaço, diretamente com um controlador é algo praticamente impraticável, os displays contam com um driver. O driver para LCD mais comum é o HD44780, estando presente na grande maioria dos displays 16×2 disponíveis no mercado.

HD44780

O driver HD44780 possui os caracteres mais utilizados salvos em sua memória, em endereços que seguem a tabela abaixo.


fonte:https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

Quando queremos escrever algum caracter, precisamos enviar o seu endereço para este driver. Além disso, ele pode ser operado em duas formas diferentes: no modo de 4 e no modo 8 bits.

O modo mais simples é o de 8 bits, que conecta oito pinos do seu microcontrolador com o HD44780, assim, os dados são enviados em paralelo, um bite por vez.O problema desse modo é o uso 8 pinos para enviar dados, o que limita a quantidade de pinos que podem ser usados para outras tarefas, como leitura de sensores e acionamento de atuadores.

No modo de quatro bits, enviando os dados em 2 nibbles (conjuntos de 4 bits) diferentes, primeiro o mais significativo seguido do menos significativo. Por exemplo, se queremos imprimir um “A” no display, precisamos fazer como abaixo.


Agora que já entendemos um pouco do funcionamento de nosso display, vamos aprender a controla-lo.

Montando o hardware

Começando pela montagem, usamos o modo de 4 bits, ligando os pinos de D4 ao D7 do display nos pinos de mesma numeração do Arduino. enquanto isso os pinos E, RS  e RW estão ligados nos pinos A0, A1 e A2 do arduino respectivamente. Já os pinos BLA e BLK vão ligados em 5V e GND respectivamente (esses pinos são usados para ligar o back light). O pino VO é ligado no GND por meio de um potenciômetro de 10k, para o ajuste de contraste. Finalmente temos os pinos de alimentação VDD e GND são ligados em 5V e no GND do Arduino como mostrado abaixo.

Com essa ligação feita, temos os pinos D4 a D7 ligados no PORTD do Atmega, e os pinos RS, RW e E ligados no PORTC, assim, temos o PORTD como o port usado para envio de dados, que no código chamaremos de PORT_LCD. Por outro lado, o PORTC, usado para configurações será chamado de PORT_config. 
No PORT_LCD, temos conectados os pinos nos quais enviaremos os endereços dos caracteres e as instruções para o LCD. Já no PORT_config temos “E”, que é o Enable do display, que bloqueia o envio de qualquer dado quando este esta em nivel baixo (0V). Seguido desse temos o pino R/W, que seleciona se os dados serão lidos (R/W=1) ou enviados (R/W=0) para o display. Finalmente temos o pino RS, que seleciona o registrador que sera usado para a transmissão de dados, sendo 0 para o registrador de instruções e 1 para o registrador de dados.

LCD_instruct

Com tudo já montado, podemos começar a falar do nosso código. Montamos uma biblioteca bem simples, contendo apenas 8 funções, mas que já permite um controle bem completo do display.

A primeira função da nossa biblioteca se chama “LCD_instruct()”, e o objetivo dela é mandar instruções para o display. O código dela ficou como abaixo.

// Implementa a rotina para envio de dados para o display, esses dados são enviados em duas etapas, 
//Primeiro - Os bits mais significativos são enviados, em seguida os bits menos significativos.
void LCD_instruct(uint8_t dados){
    PORT_config &= ~(1<<RS);
    PORT_LCD = ((dados & 0xF0)|(PORT_LCD & 0x0F));
    PORT_config |= (1<<Enable);
    _delay_ms(1);
    PORT_config &= ~(1<<Enable);
    _delay_ms(1);
    PORT_LCD = (((dados & 0x0F)<<4)|(PORT_LCD & 0x0F));
    PORT_config |= (1<<Enable);
    _delay_ms(1);
    PORT_config &=~(1<<Enable);
    _delay_ms(2);
}

Aqui começamos escrevendo “0” no pino RS, selecionando o registrador de instruções. Selecionamos este registrador para enviar instruções do tipo “mover cursor para posição x,y”, ou “Limpar todo o display”, entre outras.

A próxima etapa é enviar o nibble mais significativo do nosso comando, mantendo os bits menos significativos do PORT_LCD inalterados. Fazemos isso com um pouco de álgebra booleana. Primeiro aplicamos uma mascara no bite com os dados(usando &0xF0), zerando os menos significativos, e usamos um OR (com o “|”) para manter os bits menos significativos do por em seu estado atual. Fazemos isso para continuar usando o PORTD para outras funções, por exemplo para ler sensores, se comunicar com periféricos, etc.

Após isso. atualizamos o LCD com um pulso no pino “E” (Enable), enviando assim, os dados para o LCD.  Em seguida repetimos esse processo para o nibble menos significativos, mas desta vez, deslocando-os em 4 casas para a esquerda, já que apenas os bits mais significativos do port estão ligados ao LCD.

LCD_init

Agora que já temos como enviar dados para o display, esta na hora de configura-lo para uso. Fazemos isso por meio da função LCD_init(), descrita abaixo.

// Função para a inicialização do display LCD, nela define-se os bits mais significativos do PORT_LCD
//e do PORT_config como saida, em seguida envia-se os comandos para configurar o display na operação 
//no modo de 4 bit. 
void LCD_init(){
    DDR_LCD    = ((0xF0)|(DDR_LCD));
    DDR_config = ((0x07)|DDR_config);
    PORT_config &= ~(1<<RW);
    PORT_config &= ~(1<Enable);
    PORT_config &= ~(1<<RS);
    PORT_LCD = ((0x20)|(PORT_LCD & 0x0F));
    PORT_config |= (1<<Enable);
    _delay_ms(1);
    PORT_config &=~(1<<Enable);
    _delay_ms(2);
    LCD_instruct(0x28);
    LCD_instruct(0x0C);
    LCD_clear();
    
    _delay_ms(2);
}

Inicialmente definimos os pinos nos quais o LCD esta ligado como saídas. Logo após isso escolhemos o registrador de instruções no modo escrita (enviando 0 para “RS” e “R/W”) não habilitando o enable. Então configuramos o LCD para interface com 4 bits, enviando 0x20 para o registrador de instruções dando um pulso no Enable. Note que não usamos a função “envia_dados()”, pois o modo de operação é definido nesse passo, assim, essa função não funcionaria.

Agora que o modo de 4 bits foi definido, enviamos o comando 0x28, que comunica que o display que estamos usando possui 2 linhas, e 5×8 pontos por caracter.

Seguindo esse passo, deixamos o cursor desligado, enviando 0x0C, para o mesmo registrador, sendo que para liga-lo basta enviar 0x0E, ou caso queira ligar com blink (efeito de piscar o carácter da posição no qual ele se encontra) basta enviar 0x0F. Todas essas instruções estão presentes no datasheet do HD44780. 

LCD_clear

A função LCD_clear é a mais simples que implementamos, tanto na parte de programação, quanto ma aplicabilidade dela. O que ela faz é limpar o display, ou seja, apagar todos os caracteres que estejam sendo exibidos na tela. O seu código ficou como abaixo.

// Envia os comandos para limpar o display LCD.
void LCD_clear(){
    LCD_instruct(0x01);
    _delay_ms(1);
}

Aqui, simplesmente enviamos 1 para o bit menos significativo do registrador de instruções. Essa função pode ser aplicada por exemplo na criação de menus, limpando as informações do menu anterior para exibir as informações do próximo.

LCD_shift_display

O HD44780 tem capacidade de armazenar até 40 caracteres por linha, e como o display LCD que estamos usando só tem a capacidade de exibir 16 caracteres por vez, então é possível escrever no display sem exibir o conteúdo, ou escolher a janela de conteúdo que se deseja exibir. Esse é o objetivo da função LCD_shift_display, que foi implementada como abaixo. 

// Desloca o display para direita (dir=0) ou para esquerda (dir=1).
void LCD_shift_display(uint8_t dir){
    if(dir<1){
        LCD_instruct(0x18);
    }
    else{
        LCD_instruct(0x1C);
    }
}

Aqui, temos como entrada da função a direção, que pode ser 1 para a direita e 0 para a esquerda. O que fazemos então é enviar a instrução 0x18 para deslocamento o display a esquerda ou 0x1C para deslocar para a direita, alterando apenas o terceiro bit enviado (já que 8 em hexadecimal equivale a 1000 binário e C equivale a 1100). O interessante dessa função é que o conteúdo da memória de dados do display não é alterado, apenas o que está sendo exibido.

LCD_write_char

Essa função é bastante similar a função “LCD_instruct”, entretanto, desta vez selecionamos o registrador de dados (fazendo com que RS = 1), como abaixo.

// Escreve o caracter recebidor na posição atual do cursor
void LCD_write_char(char character){
    PORT_config |= (1<<RS);
    PORT_LCD = ((character) & 0xF0)|(PORT_LCD & 0x0F);
    PORT_config |= (1<<Enable);
    PORT_config &= ~(1<<Enable);
    PORT_LCD = (((character & 0x0F)<<4)|(PORT_LCD & 0x0F));
    PORT_config |= (1<<Enable);
    PORT_config &=~(1<<Enable);
    _delay_ms(2);
}

Aqui, começamos escrevendo 1 em RS, em seguida, enviamos o nibble mais significativo do carácter, mas mantendo os valores do nibble inferior do PORT_LCD, como já foi explicado na função “LCD_instruct”. Em seguida, após um pulso no Enable do LCD, envia-se a parte inferior do byte do caracter, deslocando-o em quatro bits para a esquerda, finalizando com um novo pulso no enable.

Os endereços dos caracteres seguem a tabela já apresenta anteriormente, mas os caracteres mais utilizados por esse display seguem a tabela ASCII, podendo ser apenas digitado normalmente entre “” na chamada dessa função. Entretanto alguns caracteres presentes não possuem correspondência com a tabela ASCII, e devem, portanto identificadas pelo código.

Assim, se quisermos escrever um “ö” (apesar de não ser algo comum), no display, deveremos enviar o código 0b11101111, em binário, ou 0xBF em hexadecimal ou 239 em decimal.

 

f – LCD_write

Esta função escreve palavras ou frases inteiras no display LCD, para isso, ela recebe uma string com texto que será escrito, em seguida ela envia cada caracter da string para a função “LCD_write_char”. Esta função ficou como abaixo.

// Envia todos os caracteres de uma string por meio de chamadas sucessivas da função LCD_write_char.
void LCD_write(char palavra[40]){
    int i=0;
    
    while (palavra[i]!='\0'){
        LCD_write_char(palavra[i]);
        i++;
    }

}

Aqui, realizamos uma varredura por cada caracter, procurando encontrar o “\0”, que é uma marcação do C que indica o final de uma string, assim, cada valor diferente desse é um caracter que deve ser impresso.

g – LCD_move_cursor

A próxima função criada é usada para selecionar a posição no display na qual o caracter será escrito, ou seja, ela moverá o cursor até a posição desejada, ela ficou como abaixo.

// Movimenta o cursor para a posição (x, y) desejada.
void LCD_move_cursor (uint8_t x, uint8_t y){
    LCD_instruct ((0x80|(y<<6))+x);
}

Nessa função, apenas selecionamos o registrador de instruções e enviamos um byte. Esse byte é da forma 1yxxxxxx, sendo que o bit mais significativo indica que se esta movendo o cursor. O imediatamente a sua direita seleciona a linha, começando a contagem em 0 na linha de cima, ou seja, 0 seleciona a primeira linha enquanto 1 seleciona a segunda. Os demais bits são para selecionar a posição do cursor na linha indo de 0 (000000) até 39 (100111), totalizando assim as 40 casas.

h – LCD_create_custom_char

Finalmente chegamos na última e mais divertida função criada para essa biblioteca, que é uma função para se criar um caracter customizado. Na tabela com os caracteres, temos que os códigos de 0b00000000 a 000000111, ou seja, entre 0 e 7 são destinados ao CGRAM, que é a memória RAM do gerador de caracteres, assim, usamos esses endereços para armazenar os caracteres que criaremos. O que resta fazer é enviar a “arte” dos caracteres, para isso, enviaremos linha a linha quais são os pontos que devem ser ligados, e quais devem ser desligados (como explicamos no começo deste artigo). O resultado está abaixo.

// Recebe um endereço de 0 a 7 e um ponteiro do endereço contendo um vetor com os carcteres mapeados, em seguida
// Salva o vetor no endereço recebido do CGRAM.
void LCD_create_custom_char(uint8_t endereco, unsigned char *charMap) {
    if (endereco < 8) {
        LCD_instruct(0x40 + endereco*8);
        for (int i = 0; i < 8; i++) {
            LCD_write_char(charMap[i]);
        }
    }
}

Aqui começamos verificando se o endereço está no intervalo permitido, em seguida acionamos esse endereço no CGRAM usando a função “LCD_instruct”. O próximo passo é enviar cada uma das linhas do nosso caracter, e fazemos isso usando a função “LCD_write_char”, passando por todas as posições do vetor que contém o mapeamento dos pontos que devem ser acesos ou apagados.

Com essas funções apresentadas conseguimos controlar o LCD com bastante flexibilidade, então se você tem interesse em usar essa biblioteca no seu projeto, você pode baixa-la do nosso github. Lá você encontra os arquivos .h e .c necessários para faze-la funcionar, além disso, la também um código exemplo mostrando como usar a nossa biblioteca. 
O que você achou dessa biblioteca? Conte pra gente nos comentários. 

0

Projetos

O LED é um componente que traz muitas possibilidades de uso, pois ele serve tanto pra dar um toque para algum projeto, como também podemos criar coisas utilizando apenas eles e aproveitando para soltar a imaginação brincando com suas cores, variando formatos e caprichando na programação com os estilos de piscar. Por isso hoje trouxemos pra vocês um cubo de LEDs! Vamos começar então pela parte do funcionamento.

Funcionamento do cubo de leds

Note que os LEDs dessa montagem estão dispostos como uma matriz 3×9. Além disso, todos os LEDs de uma linha estão conectados pelos seus catodos, enquanto os LEDs de uma coluna estão ligados pelos seus anodos. Dessa forma, se queremos selecionar um LED específico, basta selecionar uma linha e uma coluna.


Analogamente para se selecionar os LEDs do cubo basta selecionar seu nível e sua coluna. Dessa forma podemos usar o arduino para selecionar posições no cubo de led. Assim, quando queremos acender determinado LED, devemos acionar a saída correspondente a linha, ou ao “andar” no caso do cubo, e a coluna específica, enquanto garantimos que as demais linhas se mantenham em zero. O código abaixo mostra como fazemos para acender o LED do centro do cubo.


void setup(){
   pinMode(A1, OUTPUT);
   pinMode(6 , OUTPUT);
}
void loop(){
   digitalWrite(A1, HIGH);

   digitalWrite(6 , HIGH);
}


Nesse trecho de código, chamamos a atenção para o fato que as linhas e as colunas estão enumeradas de acordo com a ordem em que elas estão conectadas no arduino. Além disso, destacamos que ao agrupar os leds em níveis, conseguimos reduzir a quantidade de portas usadas pelo arduino de 27 para 12. O nome desta técnica é multiplexação e ela é muito usada em eletrônica para reduzir o número de barramentos usados na transmissão de dados. Agora que já entendemos o funcionamento do cubo de LEDs, vamos para uma das partes mais legais: a montagem!

Montagem


Antes de tudo, precisamos ter em mãos os seguintes materiais:

  1. LEDs (27 unidades);

  2. Protoboard;

  3. Resistores de 330R (9 unidades);

  4. Resistores de 10k (3 unidades;

  5. Transistores 2N2222 (3 unidades);

  6. Fios para as ligações;

Com esses materiais em mãos, a primeira parte desse projeto é montar o cubo, para isso vamos construir uma guia, nela os leds serão posicionados lado a lado, com espaços constantes entre eles, assim conseguiremos montar o cubo de forma rápida, garantindo que seus lados fiquem retos. Essa guia consiste em um quadrado de 50mm de lado com nove furos com espaços de 20 mm entre eles. Cada furo deverá ter o diâmetro do led, que para o nosso caso é de 5 mm.

Para construir essa guia você pode fazer do modo que desejar, mas nós usamos uma impressora 3D. Assim, primeiro desenhamos ela com o tinkercad e geramos o desenho no formato STL deste link, em seguida usamos o Cura para criar o arquivo .gcode. Por fim imprimimos esse arquivo usando a nossa impressora 3D. A peça ficou como mostrado abaixo.



Agora que temos essa peça em mãos, posicionamos um led em cada furo, e soldamos os seus catodos com o do led ao lado, formando uma pequena grade, como mostra a figura abaixo, essas grades serão usadas para confeccionar os andares do nosso cubo, portanto precisamos de três peças deste tipo.



Finalmente usando algum tipo de apoio (no nosso caso usamos um pedaço de papelão), soldamos as três grades uma sobre a outra, unindo apenas os anodos dos leds de cima aos de baixo, o que nos deixa com um cubo de 3 níveis e 9 colunas, como as imagens abaixo.



Circuito de controle


Para realizar as conexões do circuito de controle pegamos um pedaço de cabo flat de 12 vias e o separamos em 4 pedaços de 3 vias, assim conseguimos soldar estes pedaços a cada nível e a cada coluna do cubo de led, ficando um pedaço para cada linha de colunas do cubo, enquanto o último pedaço é soldado nos níveis, conforme vocês podem ver na imagem abaixo.


Para os cabos soldados nas colunas, a outra extremidade é ligada nas saídas numeradas de 2 até 10 de um Arduino, com cada via passando antes por um resistor de 330Ω. Já para os 3 níveis conectamos cada via ao coletor de transistores NPN 2N2222. Esses transistores possuem seus emissores aterrados, enquanto suas bases estão ligadas às saídas A0, A1 e A2 do Arduino por meio de resistores de 10k. Isso está representado na imagem abaixo.




Agora que já montamos nosso cubo e já sabemos o básico sobre o seu funcionamento, vamos começar a criar e explorar alguns efeitos que podemos criar com ele!


Explorando com o cubo

1 – Função “seleciona_nivel()”


Essa função é bem simples e o objetivo dela é escrever como alto o nível selecionado, enquanto demais são mantidos em nível lógico baixo. Ela ficou implementada como segue:


void seleciona_nivel(int nivel){
   for (int i = 0; i < 3; ++i){
      digitalWrite(niveis[ i ], LOW);
}
   if (nivel >= 1 && nivel <= 4){
      if (nivel != 4) {
         digitalWrite(niveis[nivel - 1], HIGH);
      }
      else{
         for (int i = 0; i < 3; ++i){
            digitalWrite(niveis[ i ], HIGH);
         }
      }
   }
}

Aqui começamos escrevendo todos os níveis como baixo, em seguida é verificado se o valor de nível escolhido está no intervalo correto, se não está, então nada é realizado, apenas se mantêm os níveis como baixo, mas se ele está no intervalo correto, então o nível selecionado é escrito como alto. Observe que se o nível selecionado for 4 então todos os níveis são descritos como alto.

Veja que o nível foi acionado por meio de um vetor chamado “niveis[ i ]”, esse é um vetor de 3 posições com cada nível do cubo descrito nele. Além desse vetor, temos outro para as colunas (com nove posições) e uma matriz para os estados do cubo.


2 – Função “envia_estados()”


A segunda função implementada é a função enviar estados. Ela funciona com o auxílio da variável global “int estados[3][9];”, e é responsável por enviar os valores dessa matriz para a saída, então o que ela faz é simplesmente realizar uma varredura da matriz e enviar os valores dela para as saídas correspondentes do Arduino. Ela ficou como mostra abaixo.


//Envia a matriz que representa os niveis dos led para o cubo.
//Pode realizar uma varredura nivel a nivel (varredura 1) ou
//habilitar todos os niveis simultaneamente (varredura 2).
void envia_estados(int estados[ 3 ][ 9 ], int varredura){
   if(varredura==1){
      for(int i=0; i<=2; i++){
         for(int j=0; j<9; j++){
            digitalWrite(colunas[ j ], estados[ i ][ j ]);
            seleciona_nivel(i+1);
         }
      }
   }

   else if(varredura==2){
      for(int j=0; j<9; j++){
         digitalWrite(colunas[ j ], estados[ 0 ][ j ]);
      }
      seleciona_nivel(4);
   }
}

O código começa verificando o modo de varredura desejado, ou seja, se é desejado realizar uma varredura nível a nível, ou se o objetivo é controlar as colunas apenas. Se o modo escolhido for o 1, então temos dois loops, o primeiro acionando as colunas e o segundo selecionando o nível, enviando assim todos os valores da matriz para a saída. Entretanto, se a opção foi a 2 então temos apenas um loop lendo as colunas, e o único nível enviado para a saída é o nível 0 da matriz.


3 – Função “acende_limpa_cubo()”


Essa função é bem simples, ela é formada apenas pelos loops para a escrita em matriz, como mostrado abaixo:



//Função torna alto ou baixo o nivel lógico de todos os leds do cubo.
void acende_limpa_cubo(int on_off){

   for(int i=0; i<4; i++){
      for(int j=0; j<9; j++){
         estados[i][j]=on_off;
      }
   }
}

Nessa função temos que o valor de “on_off” é escrito em todas as posições da matriz “estados[ 3 ][ 9 ]”, assim, se passarmos 1 para esse função e posteriormente enviar essa matriz para a função “envia_estados()”, o cubo acenderá completamente, mas ele apagará completamente se o valor de on_off for 0.


4 – Função “led_aleatorio()”


Como o nome já diz, o objetivo dessa função é acender um led aleatório, ou seja, colocar em alto uma posição aleatória na matriz estados. Para isso usamos a função “random()” do Arduino para selecionar a posição aleatória, como no código abaixo.


//Acende ou apaga um led aleatório do cubo.
void led_aleatorio(int pausa, int on_off){
   estados[random(0,3)][random(0,9)]=on_off;
   for(int tempo=0; tempo<pausa; tempo++){
      envia_estados(estados, 1);
   }
}

Essa função recebe dois valores, a “pausa” determina por quanto tempo o led ficará aceso, porém, diferente de outros lugares do código, aqui não usamos a função “delay()” e sim um “for”. Fazemos isso porque o delay iria pausar a execução do código, mantendo apenas o último comando da função “envia_estados()’, por outro lado o “for” executa essa função completamente, várias vezes, dando a impressão de que o programa foi pausado.

O outro parâmetro recebido pela função é o “on_off”, que assim como o parâmetro de mesmo nome atua na função “acende_limpa_cubo()”, determina se os leds serão ligados ou desligados.


5 – Função “Plano_vertical()”


A próxima função que trazemos aqui é a função “plano_vertical()”, nessa função acendemos leds que estão posicionados lado a lado, gerando planos verticais. O código ficou como abaixo:


//Acende conjuntos de 3 colunas do cubo, formando planos.
void plano_vertical(int plano){
   if(plano>=1 && plano<=3){
      for(int i=0; i<3; i++){
         for(int j=(3*(plano-1)); j<(3*plano); j++){
            estados[i][j]=1;
         }
      }
   }
   else if(plano>=4 && plano <=6){
      for(int i=0; i<3; i++){
         for(int j=(plano-4); j<(plano+3); j+=3){
            estados[i][j]=1;
         }
      }
   }
}

Primeiramente os planos que essa função gera estão enumerados de 1 a 6, sendo que os de 1 a 3 são paralelos ao plano frontal e os de 4 a 6 são perpendiculares a estes. Assim, essa função recebe o parâmetro “plano” para selecionar o plano que será acionado, e por meio dos loops adequados ele envia nível alto para a matriz de estados.


6 – Função “gira_led()”

Agora vamos falar da última função criada para controlar o cubo, a função “gira_led()”.A ideia por trás dessa função está em usar uma matriz 4×9, sendo que em cada linha armazenamos os níveis lógicos de todas as colunas de led, dessa forma ao alternamos entre as linhas dessa matriz, os leds em nível alto irão aparentar ser deslocados.

Assim podemos gerar um efeito de rotação usado um loop que envia os valores presentes na linha para saída, e em seguida passa para a próxima linha após uma breve pausa.

Essa função ficou como abaixo:


//Função cria o efeito de leds girando em torno da coluna
//central. A quantidade de voltas pode ser inserida e o sentido
//de rotação pode ser horário ou anti-horário, sendo para horário
//e 2 para anti-horário.
void gira_led(int pausa, int voltas, int sentido) {
   // Padrões de ligar/desligar dos LEDs para cada passo do movimento
   const int padrao_horario[4][9] = {
      {LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW},
      {LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, LOW, LOW},
      {HIGH, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, HIGH},
      {LOW, HIGH, LOW, LOW, HIGH, LOW, LOW, HIGH, LOW}
   };
   const int padrao_antihorario[4][9] = {
      {LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW},
      {LOW, HIGH, LOW, LOW, HIGH, LOW, LOW, HIGH, LOW},
      {HIGH, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, HIGH},
      {LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, LOW, LOW}
   };

   int (*padrao)[9]; // Ponteiro para o padrão de acordo com o sentido
   if (sentido == 1) {
      padrao = padrao_horario;
   }
   else {
      padrao = padrao_antihorario;
   }
   for (int i = 0; i < voltas; i++) {
      for (int j = 0; j < 4; j++) {
         // Define o estado dos LEDs de acordo com o padrão atual
         for (int k = 0; k < 9; k++) {
            digitalWrite(colunas[k], padrao[j][k]);
         }
         delay(pausa);
      }
   }
}

Para essa função enviamos os valores de “pausa”, “voltas” bem como o “sentido”.

Começando pela “pausa”, esse parâmetro define o tempo entre um estado e outro, assim, se quisermos um efeito de rápida rotação passamos um valor baixo para a “pausa”. O parâmetro “voltas” define a quantidade de vezes que o efeito se repete, ou seja, se for enviado 10 para “voltas”, esse efeito será repetido 10 vezes. Finalmente o “sentido” de rotação é definido, sendo que 1 é para sentido horário e 2 para anti-horário.


Loop Principal


Finalmente vamos falar do nosso loop principal, nele, o que fazemos é usar as funções já apresentadas para criar diversos efeitos com o cubo. Por exemplo, o trecho abaixo apresenta uma forma de acender o cubo, acendendo leds de forma aleatória:


for(int n=0; n<100; n++){ //Cubo acendendo 1 led por vez, de forma aleatória
   led_aleatorio(500,1);  //até todos os leds serem acesos.
}
acende_limpa_cubo(1);
for(int n=0; n<1000; n++){
   envia_estados(estados, 1);
}

Aqui executamos a função “led_aleatório()” 100 vezes, assim o cubo vai acendendo lentamente, um led por vez. Entretanto para garantir que qualquer led que não tenha sido aceso ainda acenda, ao final da execução dessa função, usamos a função “acende_limpa_cubo(1)”. Para apagar o cubo lentamente podemos usar o mesmo código, apenas mudando o valor de “on_off” enviado para as funções “led_aleatório(500, on_off)” e “acende_limpa_cubo(on_off)”, ou seja mudar o valor de “on_off” para 0.

Outro efeito que podemos é fazer planos “caminharem” pelo cubo, para isso a função “planos_venticais()” é usada como mostrado abaixo.


acende_limpa_cubo(1);
for(int nivel=0; nivel<4; nivel++){ //"planos" subindo e descendo.
   seleciona_nivel(nivel);
   delay(250);
}
seleciona_nivel(0);
delay(250);
for(int nivel=3; nivel>=0; nivel--){
   seleciona_nivel(nivel);
   delay(250);
   if(nivel==0){
      acende_limpa_cubo(0);
      envia_estados(estados, 2);
   }
}

for(int n=0; n<3; n++){ //"planos" verticais se movendo para lateralmente.
   plano_vertical(n+4);
   envia_estados(estados,2);
   delay(250);
   acende_limpa_cubo(0);
   envia_estados(estados, 2);
}

delay(250);

for(int n=0; n<3; n++){
   plano_vertical(6-n);
   envia_estados(estados,2);
   delay(250);
   acende_limpa_cubo(0);
   envia_estados(estados, 2);
}

for(int n=0; n<3; n++){ //"planos" verticais se movendo para tras e para frente.
   plano_vertical(n+1);
   envia_estados(estados,2);
   delay(250);
   acende_limpa_cubo(0);
   envia_estados(estados, 2);
}

delay(250);

for(int n=0; n<3; n++){
   plano_vertical(3-n);
   envia_estados(estados,2);
   delay(250);

   acende_limpa_cubo(0);
   envia_estados(estados, 2);
}

A princípio temos a função “acende_limpa_cubo(1)” mantendo todos os valores da matriz de estados em alto, em seguida, os dois laços de repetição alternam os níveis acionados, causando o efeito de “subir e descer”. Os laços seguintes usam a função “planos_verticais()” para produzir um efeito similar nas demais direções.

O ultimo efeito do qual vamos falar usa a função “gira_led()”, ao contrário das funções anteriores essa não necessita de um loop() para se realizar um efeito, sendo que ela foi aplicada como abaixo:


seleciona_nivel(3); //Efeito de leds girando uma vez em cada nivel,
gira_led(75, 15, 2); //alternando o sentido.
seleciona_nivel(2);
gira_led(75, 15, 1);
seleciona_nivel(1);
gira_led(75, 15, 2);

Nesse trecho de código gera o efeito de rotação uma vez em cada nível em sentidos alternados, ou seja, no sentido anti-horário nos níveis 1 e 3 e no sentido horário no nível 2, gerando 15 rotações por nível.

Enfim, o código deste projeto bem como o de outros projetos do nosso blog podem ser encontrados no nosso github.

Não esqueça de contar para a gente o que você achou desse projeto nos comentários e qual o próximo que vocês querem ver por aqui!!!

0

Projetos


Os hamsters são animais conhecidos por seu habito de correr incessantemente em suas rodinhas, desse modo é normal se perguntar que velocidade eles alcançam, ou ainda, querer saber que distancia eles percorrem. Pensando nisso construímos um velocímetro e contador de voltas para rodinhas de hamsters. Curioso para saber como? Não se preocupe que contamos neste post.

Antes de mais nada vamos aos materiais usados, que são os seguintes:

  1. Arduino Uno;

  2. Display LCD 16×2;

  3. Sensor infravermelho TCRT5000;

  4. Resistores de 330Ω, 3,3kΩ e 10kΩ (1 de cada);

Montando o velocímetro

A ideia aqui é usar um sensor infravermelho TCRT5000  para encontrar o momento em que o hamster completa uma volta na sua rodinha, e com o tempo de cada volta, juntamente ao tamanho da rodinha calcularmos a velocidade e a distancia percorrida por ele, para em seguida exibir esses valores em um display LCD. Para isso, vamos montar o circuito como mostrado na figura abaixo.

Nessa figura temos que o display LCD esta ligado com os pinos D4 a D7 nos pinos de mesmo nome do Arduino (ou seja, nos pinos PD4 a PD7 no Atmega328p), além disso, os pinos  E, R/W e RS estão conectados nos pinos A0, A1 e A2 (ou PC0, PC1 e PC2) respectivamente. Para a alimentação do display, assim como o TCRT5000, esta sendo utilizado os 5V fornecidos pelo próprio Arduino.

O funcionamento do TCRT5000 se da por meio de um fototransistor acoplado a um LED infravermelho, assim, quando um objeto é posto próximo desse sensor, a luz infravermelha é refletida para o fototransistor, que atuara como um circuito fechado. No nosso circuito, o emissor do fototransistor esta conectado ao terra (gnd), portanto quando ele atuar como um circuito fechado temos que a tensão no coletor do fototransistor e consequentemente pino D8 do Arduino (ou PB0 para o Atmega, pino onde o conector esta ligado) sera de 0V. Já quando não existir nenhum objeto próximo ao TCRT5000, o fototransistor funciona como um circuito aberto, e a tensão de seu coletor sera de 5V, ja que o mesmo esta ligado nessa tensão por meio de um resistor de 10kΩ.

Programando nosso velocímetro

Agora que já temos os materiais em mãos e montamos o nosso circuito, iremos programar o Arduino. O código completo usado nesse projeto, além da biblioteca usada para controlar o LCD estão presentes no nosso GitHub.

O que vamos fazer no código é usar o timer0 para nos fornecer uma medida de tempo para o calculo da velocidade de nosso velocímetro, para isso, precisamos que ele seja reiniciado a cada volta completada pelo hamster. Com esse fim vamos usar a interrupção por mudança de estado do pino PB0, assim, sempre que o microcontrolador receber um sinal do TCRT5000, ele sofrera uma interrupção, e nela ocorrera a contagem da quantidade de voltas dada pelo Hamster e a inicialização do timer, sendo que após duas interrupções suscetivas, ou seja, duas voltas completas, a velocidade é calculada. Entretanto, se a rodinha ficar inativa por muito tempo (mais que 10 segundos), usaremos a interrupção por estouro do timer para desliga-lo.

Para simplificar a escrita e a compreensão do nosso código, usamos 5 funções (além da main e das interrupções), que servem para ligar o LCD, configurar o pino PB0 como entrada habilitando as interrupções, configurar o timer0, realizar os cálculos necessários e criar um loop que mantem o circuito sempre em funcionamento. Cada função usada esta explicada abaixo.

Função “Liga_LCD()”

A primeira função que vamos discutir é a função “Liga_LCD()”, ela foi implementada como presente abaixo.

void Liga_LCD(void){
          LCD_init();
          LCD_clear();
          LCD_move_cursor(0,1);
          LCD_write("Hodometro para");
          LCD_move_cursor(1,4);
          LCD_write("Hamsters");
          _delay_ms(2000);
          LCD_clear();
}

Nela iniciamos o display usando “LCD_init()”, então limpamos o display com “LCD_clear()” e finalmente a mensagem “Hodometro para Hamsters” é exibida por dois segundos com as funções “LCD_write()”, “LCD_move_cursor()” e “_delay_ms()”. 

A função LCD_init() envia um conjunto de instruções que configuram o display no modo de 4 bits com o cursor ligado. Enquanto isso a função LCD_clear() apaga tudo o que esta exibido no display e retorna o cursor para a primeira posição da primeira linha. Já a função LCD_move_cursor() posiciona o cursor na posição desejada no display, essas funções fazem parte da biblioteca LCD.h. Já a ultima função _delay_ms() pertence a biblioteca <util/delay.h> e pausa a execução do programa pelo tempo desejado.

Função “configura_contador()”

A próxima função configura PB0 como entrada no registrador DDRB, em seguida, o pull up é ativado, isso mantém o nível de tensão do pino PB0 em 5V, a menos que a entrada seja 0V, o que facilita a identificação de transições para baixo. Logo após, usamos os registradores PCICR e PCMSK0, para habilitar as interrupções por mudança de estado do pino PB0, assim, sempre que o pino PB0 muda de estado, ocorre uma interrupção. Essa função ficou como mostra o exemplo abaixo:

void configura_contador(void){
          DDRB &= ~(1<<PB0);
          PORTB |= (1<<PB0);

          PCICR |= (1<<PCIE0);
          PCMSK0 |= (1<<PCINT0);
}

A rotina da interrupção para a mudança de estado do pino PB0 em primeiro lugar realiza a leitura desse pino, e se este sofreu transição para nível alto, apenas um pequeno delay de 50ms é realizado, mas se a transição é para nível baixo, então o timer0 é reiniciado, a quantidade de voltas é incrementada, e o valor do tempo é salvo na variável “tempo”. Essa interrupção foi implementada como mostrado a seguir:

ISR(PCINT0_vect){
          if(PINB & (1<<PB0)){
          _delay_ms(50);
          }
          else{
                    voltas +=1;
                    voltas_total +=1;
                    tempo = i;
                    i = 0;
_delay_ms(50);

TCCR0B |= (1<<CS00);
TCNT0 = 0; } }

Função “configura_timer()”

Da mesma forma que a função configura contador, essa função configura uma interrupção, mas desta vez por estouro do timer0. Para isso, primeiro configura-se o timer0 no modo de operação normal e desconectado. Essa configuração foi selecionada, pois com ela é possível obter uma precisão de 0,16ms, que é o suficiente para a construção do velocímetro. Por fim a interrupção por estouro do timer é habilitada. A função que configura o timer esta presente abaixo.

void configura_timer(void){
          TCCR0A = 0;
          TCCR0B = 0;
          TIMSK0 |= (1<<TOIE0);
}

Com essa configuração temos 62500 interrupções por estouro por segundo, sendo que a cada interrupção a variável “i” é acrescida em 1 e quando ocorre uma interrupção por mudança de estado do pino PB0, o valor de “i” é salvo em “tempo” e “i” é reiniciada com zero. Entretanto, se não ocorre nenhuma mudança de estado em PB0 dentro de 10 segundos, então i>624999 e quando isso ocorre, a interrupção desconecta o timer e reinicia as variáveis “voltas” e “i”. Essa interrupção está abaixo.

ISR(TIMER0_OVF_vect){
          i++;
          if (i>624999){
                    i=0;
                 
                    voltas = 0;
                    TCNT0 = 0;
                    TCCR0B &= ~(1<<CS00);
          }
}

Ao se desconectar o timer o Arduino fica aguardando uma interrupção por mudança de estado do PB0 para reiniciar o timer. Além disso o velocímetro é zerado.

Função “calcula_velocidade()”

O código desta função está abaixo:


void calcula_velocidade(float tempo, float raio){
    if (voltas > 1){
        tempo = tempo/(62500);
        distancia = (voltas_total-1)*2*3.1416*raio;
        velocidade = 3.6*2*3.1416*raio/tempo;
    }
    else {
        velocidade = 0;
    }
}

Esta função é a responsável pelo calculo da velocidade do hamster em km/h. Para isso, primeiro é verificado se houveram 2 incrementos na variável “voltas”, uma vez que na primeira vez que o TCRT5000 é ativado, o timer está zerado. Se já houveram 2 incrementos em voltas, então o tempo em segundos é calculado usando:

(1)   \begin{equation*} tempo=\frac{tempo}{62500} \end{equation*}

Com o tempo em segundos calculamos a velocidade com a equação abaixo.

(2)   \begin{equation*} velocidade=3.6*\frac{2*\pi*raio}{tempo} \end{equation*}

Nessa equação temos que 2*\pi*raio é o comprimento da circunferência da rodinha do HHamster, ou seja, a distancia que ele percorre em uma volta completa. Assim a velocidade será obtida dividindo este valor pelo tempo. O fator 3.6 é usado para converter a velocidade (encontrada em metros por segundo) para quilômetros por hora.

Além disso, para calcular a distancia percorrida pelo Hamster basta multiplicar a circunferência de sua rodinha pelo numero de voltas dada, como abaixo.

(3)   \begin{equation*} distância=2*\pi*raio*numero-de-voltas \end{equation*}

Função “loop()”

Por fim temos a função loop(), nossa ultima função, que ficará sempre em execução, sendo implementada como abaixo:



void loop(void){
    char str_distancia[20];
    char str_velocidade[20];
    int vel_int;
    int vel_dec;
    int distancia_int;
    int distancia_dec;
   
    while(1){
                calcula_velocidade(tempo, raio);
                distancia_int = floor(distancia);
                distancia_dec = 100*(distancia-distancia_int);
                sprintf(str_distancia, "Dis: %d,%d[m]   ", distancia_int, distancia_dec);
                vel_int = floor(velocidade);
                vel_dec = 100*velocidade - 100*vel_int;
                sprintf(str_velocidade, "Vel: %d,%d[km/h]   ", vel_int, vel_dec);
                LCD_move_cursor(0,0);
                LCD_write(str_distancia);
                LCD_move_cursor(1,0);
                LCD_write(str_velocidade);
                voltas_cont=voltas;
         }
    }   
}

Nessa função, primeiro temos a declaração das variáveis a serem utilizadas sendo duas strings e quatro variáveis int. Em seguida, temos um loop infinito (while(1)), nele utiliza-se a função calcula_velocidade() para se calcular a velocidade e a distancia total percorridas pelo Hamster. Em seguida, estes valores são gravados em números inteiros, primeiro a parte inteira, depois as duas primeiras casas depois da virgula. Isso porque a função sprintf, usada para transformar números em strings, não funciona com variáveis do tipo float. Finalmente esses valores são exibidos no display do nosso velocímetro.

Função “main()”

Por fim, temos a função main do nosso programa, que ficou como a seguir:

void main(void){   
    SREG   |=  (1<<7);
    voltas  = 1;
    Liga_LCD();
    configura_contador();
    configura_timer();

    loop();
}

Na função main, apenas habilita-se as interrupções globais e chama-se as funções Liga_LCD(), configura_contador(), configura_timer() e loop().

Testando

Para testar o nosso projeto contamos com a ajuda do Antônio, o nosso hamster atleta, e ao final de uma noite ele correu por 3200 metros. No link abaixo você encontra o nosso projeto funcionando.
https://youtube.com/shorts/fL2wfnrB3ng?feature=share
E então, o que vocês acharam deste projeto? Conte pra gente nos comentários.


0

Conhecimento

Os encoders são botões giratórios, similares aos potenciômetros, e que podem ser girados indefinidamente. Eles estão presentes em rádios, mouses, impressoras 3D, equipamentos de bancada, entre vários outros dispositivos eletrônicos. Assim, entender o seu funcionamento e aprender a usa-lo é algo muito interessante e útil, por isso vamos te ensinar mais sobre eles!

Podemos classificar os encoders em dois tipos, os absolutos e os incrementais. Aqui não iremos nos aprofundar muito nos encoders absolutos, mas vale apena uma breve explicação sobre eles. De modo simplificado, os encoders absolutos são construídos com um disco com diversas trilhas, cada uma com um sensor e uma marcação específica de modo que, ao se combinar as informações captadas pelos sensores de todas as trilhas é possível se formar um código, que é único para a posição em que o encoder se encontra, ou seja, em um encoder absoluto podemos determinar exatamente sua posição através de um código.

O segundo tipo de encoder, o incremental é um pouco mais simples. Ele é formado por uma unica trilha com marcações igualmente espaçadas e dois sensores postos lado a lado, assim, quando o seu disco é girado, com o auxilio do seu eixo, temos a ordem com a qual os sensores são ativados e que dependem do sentido de rotação.

Conhecendo o encoder incremental

Um exemplo de encoder incremental é o EC11, que é o modelo que usaremos neste artigo. Ele possui cinco terminais, sendo dois deles usados por uma chave, que pode ser ativada pressionando o seu eixo. Enquanto isso os demais terminais são usados para controlar o encoder. A imagem abaixo representa o simbolo esquemático de um encorder desse tipo.

Aqui temos os terminais S1 e S2 para a chave, enquanto os terminais A e B permitem acesso a saída dos sensores. O terminal C é o comum aos dois sensores e geralmente é aterrado. O acionamento desse encoder ocorre de forma mecânica, sendo que, quando giramos o seu eixo, o contato entre um dos terminais das extremidades (A ou B) é fechado com o terminal comum (C). Como já foi dito, a ordem com a qual os sensores são acionados dependem do sentido de rotação do encoder, assim, se ele é rotacionado no sentido horário, sabemos que o contato entre A e C é fechado e em seguida entre B e C. Mas o inverso ocorre se girado no sentido anti-horário, quando fecha-se primeiro o contato entre B e C, e depois entre A e C.

Isso pode nos descrever duas ondas quadradas em quadratura, como abaixo.

Por quadratura nos referimos a diferença de fase entre os sinais, isto é, temos que um sinal esta adiantado em relação ao outro por 90° (aqui vale ressaltar que esse ângulo não se refere ao ângulo físico do encoder, mas sim a defasagem dos sinais).

Mapeando as transições do encoder

As ondas apresentadas mostram os sinais nos canais A e B de acordo com a posição do encoder. Se percorrermos as ondas da esquerda para a direita vemos que o sinal A está adiantado em relação ao sinal B. Isso ocorre quando giramos o encoder no sentido horário. Entretanto, se percorrermos as ondas da direita para a esquerda, veremos que o sinal B fica adiantado em relação ao A, o que acontece quando giramos o encoder no sentido anti-horário. Assim, para saber o sentido de rotação, precisamos apenas encontrar o sinal adiantado.

Agora que conhecemos seu funcionamento, pensamos em como identificar o sinal adiantado. Com as formas de onda em mãos, podemos mapear as transições realizadas, ou seja, encontrar todas as transições possíveis e determinar em quais condições ela ocorre. 

Vamos usar como exemplo o caso em que A e B estão em nível alto, ou seja, AB=11, os próximos estados possíveis são AB=01, quando girado no sentido horário e AB=10, quando girado no sentido anti-horário. Isso fica mais claro na imagem abaixo.

Além disso, se AB permaneceu em 11 ele ficou parado, e para AB=00 temos um erro de leitura. Assim, para cada posição temos 4 estados futuros, sendo 1 deles impossível (ou um erro de leitura). Isso está sintetizado na tabela abaixo.

Nessa tabela, temos um número binário de quatro bits para cada movimento do encoder. Além disso podemos escrever horário como sendo 1, anti-horário como -1 e parado ou impossível como 0, assim conseguimos escrever um vetor de 16 posições com as transições possíveis. Esse vetor é {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0} e ele é muito útil, uma vez que podemos usa-lo para incrementar ou decrementar uma variável, como veremos logo mais.

Montando um teste

Para entender como usar um encoder preparamos um exemplo prático, mas, antes de mais nada precisamos de alguns materiais básicos, são eles:

  1. Display LCD 16X2;
  2. Arduíno;
  3. Resistores de 330Ω e de 3,3kΩ (1 de cada);
  4. Encoder rotativo EC11;
  5. LED;

O que faremos é controlar o brilho de um LED, para isso, ajustaremos a razão cíclica de um PWM gerado com um Arduino. O valor da razão cíclica será determinado pelo encoder e exibido no display LCD, assim, precisamos montar o circuito como abaixo.

 

Aqui temos os pinos 0 e 1 (PD0 e PD1 para o ATmega328p) para a entrada dos sinais A e B do encoder, enquanto os pinos de 4 a 7 (PD4 ao PD7) são conectados nos pinos de mesmo valor do LCD, para o envio de dados. Já os pinos A0, A1 e A2 (PC0 ao PC2) são conectados nos pinos En, R/W e RS respectivamente, para configurar o display. Por fim temos o pino 11 (PB3), que é por onde saíra o PWM gerado. 

Lendo o encoder

Com o hardware montado, podemos programar o Arduino. O programa completo com as bibliotecas utilizadas estão disponíveis em nosso github. Para o programa criamos uma função denominada “le_encoder()”, que como o nome já diz, é responsável pela leitura do encoder. Essa função ficou como mostrado abaixo:


int le_encoder(void){

    static int8_t A_B = 3;                                                                                     // A_B |==> Armazena os estados anterior e atual do encoder.
    static int8_t incremento;                                                                                // incremeto|==> Variavel retornada pela função, indica o sentido de rotação do encoder.
    static int8_t entradas[]= {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};          // entradas[] |==> Vetor com as mudanças de estados possíveis.

// Cria um binario de 4 bits com os 2 MSDs indicando o estado passado e os 2 LSDs indicando o estado atual do encoder.
    A_B<<=2;
    A_B |= (PIND & 0x03);
    incremento += entradas[A_B & 0x0f];

// Evita incrementos e decrementos descontrolados.
    if(incremento>1){
        incremento=0;
    }
    if(incremento<-1){
        incremento=0;
    }
    return incremento;
}

Primeiro temos a declaração das variáveis locais, sendo todas estáticas para não serem reiniciadas a cada chamada da função. Em seguida, o valor antigo da variável A_B, responsável por armazenar o estado dos pinos PD0 e PD1, sofre um deslocamento de dois bits para a esquerda (A_B<<=2) e o valor dos pinos PD0 e PD1 são lidos com o auxilio de uma mascara e colocados nos 2 bits menos significativos (A_B |= (PIND & 0x03)). Isso forma um binário que representa a mudança de estado do encoder.

Logo após, soma-se na variável incremento 1, -1 ou 0, de acordo com o valor dos quatro bits menos significativos de A_B. Para isso lê-se a posição A_B & 0X0f do vetor entradas[], que é o vetor com as transições possíveis, que já discutimos aqui. Aplica-se a mascará 0x0f para se descartar os bits mais significativos de A_B, e ler apenas os 4 menos significativos, que são formados pelo valor de A_B anterior e atual.
Finalmente temos um “if”, que verifica se o valor do incremento é maior que 1 ou menor que -1, e se sim zera esse valor. Isso evita incrementos sucessivos de forma descontrolada.

Controlando o PWM

Agora que já conseguimos identificar o sentido de rotação do nosso encoder, vamos realizar o controle do PWM. Para isso, usaremos um inteiro de 8 bits e sem sinal, assim, temos uma variável que ira de 0 a 255. Fazemos isso porque o registrador OCR2A, responsável pelo PWM no pino PB3, é de 8 bits. O código fica como segue abaixo.

void calcula_PWM(int incremento){

static uint8_t D;                    // D = duty cicle em percentual
static uint8_t duty = 0;             // duty = duty cicle valor passado para o OCR2A para dutyrole do PWM
char s_duty[20];                     // s_duty = string com o duty cicle em percentual para exibir no LCD.

if (incremento){ if(duty>10){ if(duty<245){ duty += 3*incremento; } else if(duty<253){ duty += incremento; } else if(incremento>0){ duty = 255; } else{ duty += incremento; } } else if(duty>2){ duty += incremento; } else if(incremento<0){ duty = 0; } else{ duty += incremento; } D=100*duty/255; if(duty > 254){ sprintf(s_duty, "Duty: %d%% Max ", D); } else if(duty < 1){ sprintf(s_duty, "Duty: %d%% Min ", D); } else{ sprintf(s_duty, "Duty: %d%% ", D); } OCR2A = duty; LCD_move_cursor(0,0); LCD_write(s_duty); _delay_ms(20); } }

Nessa função primeiro se verifica se existe algum incremento (ou decremento) e se sim, verifica se a variável duty (que será passada para o OCR2A) está entre 10 e 245. Em caso afirmativo será somado 3 vezes o incremento em duty, mas caso essa variável esteja entre 2 e 10 ou 245 e 253, esse incremento será de apenas uma unidade. No caso dessa variável ser menor que dois ou maior que 253 então a saída será 0 ou 255 respectivamente.

O incremento de 3 quando duty está entre 10 e 245 permite um controle veloz da razão cíclica, mas mantem o controle do brilho do led suave, já que uma mudança dessa grandeza no PWM não é perceptível. Além disso, quando o valor está próximo de 255 o controle do PWM não permite um novo incremento, já que isso acarretaria em uma transição abrupta de nível máximo para mínimo. O mesmo ocorre nas proximidades de 0.

Em seguida calculamos o valor percentual da razão cíclica na variável D, exibindo esse valor no display LCD na forma “Duty: XXX”. Além disso, imprimimos na tela Máx quando a razão cíclica é 100% e Min quando essa é 0%. Por fim enviamos o valor de duty para o registrador OCR2A.

Main

Finalmente temos a função main, que realiza a configuração das entradas e saídas, além de configurar o PWM. Ela ficou como abaixo:


int main(void){
// Configura os pinos de entrada. 
    DDRD &= ~((1<<PD0)+(1<<PD1));
    PORTD |= ((1<<PD0)+(1<<PD1));

// Configura PWM.
    DDRB |= (1<<PB3);
    TCCR2A |= ((1<<COM2A1)+(1<<WGM21)+(1<<WGM20));
    TCCR2B |= (1<<CS20);
    OCR2A = 0;

// Inicia o LCD.
    LCD_init();
    LCD_move_cursor(0,0);

// Loop de execução.
    while(1){
        calcula_PWM(le_encoder());
    }
return 0;
}

O que fazemos aqui é configurar os pinos PD0 e PD1 como entradas (no registrador DDRD) e habilitar o resistor de pull up interno (no PORTD). Em seguida, habilitamos PB3 como saída (com DDRB), e configuramos o timer 2 no modo fast PWM, sem prescale. Aqui também iniciamos o LCD, configurando as entradas e saídas pertinentes com a função LCD_init(), e posicionamos o cursor na primeira casa e na primeira coluna. Finalmente, temos um loop infinito que realiza a constante chamada da função calcula_PWM().

Nós montamos este circuito para testarmos na prática e ele ficou funcionando como no vídeo deste link. O que você achou desse artigo? Pretende usar o encoder em algum projeto? Nos conte aqui nos comentários.

0