Um problema que ocorre quando devemos acionar muitas cargas, por exemplo, uma matriz de LEDs com circuitos digitais comuns ou microcontroladores é que suas saídas são limitadas. No entanto, com artifícios e circuitos digitais adicionais relativamente podemos excitar uma enorme quantidade de cargas sem muitos problemas. Veja como fazer isso neste interessante artigo, destinados principalmente aos makers interessados em efeitos visuais com LEDs.
Acender alguns LEDs com um circuito integrado de efeitos ou um Arduino é simples. Assim, o desenvolvimento de circuitos de efeitos, IoT e vestíveis usando efeitos de LEDs é algo que qualquer pode fazer sem muitos problemas.
No entanto, se precisarmos acionar muitos LEDs ou outras cargas com um circuito digital limitado, ou um microcontrolador mais simples como o Uno ou a Lillypad a coisa complica.
Como acionar 36 LEDs ou 36 cargas usando as saídas de um Arduino Uno ou uma placa para vestíveis Lillypad sem ocupar todas as saídas, pois precisaremos de algumas delas adicionamento para outras finalidades?
A saída para isso existe com circuitos relativamente simples que podem ser acionados ao seu projeto e que fazem uso da técnica do acionamento matricial que já usamos há mais de 50 anos com os circuitos lógicos TTL e CMOS. Vamos ensinar exatamente o que é isso partindo justamente de algumas configurações antigas que já usamos numa ampla quantidade de projetos de nosso site, de nossos livros e de muitos artigos que publicamos em revistas técnicas no passado.
Acionamento Matricial
A ideia básica é simples sendo analisada numa configuração de apenas 4 LEDs que mostramos na figura 1.
O que fazemos é colocar esses LEDs numa matriz de 2 x 2 de tal forma que o acionamento deles é feito pela combinação das duas saídas possíveis: linhas e colunas.
Assim, para acionar o LED 3 da nossa matriz, devemos colocar a saída 1 das colunas (C1) no nível baixo e a saída 2 das linhas (L2) no nível alto. Desta forma, conforme mostra a figura 2, a corrente circulará apenas pelo LED3.
Mas, isso não significa que só podemos acionar um LED de cada vez. Conforme mostra a figura 3, se colocarmos as linhas 1 e 2 no nível alto e, ao mesmo tempo, a coluna 2 no nível baixo, podemos acionar os LEDs 2 e 4 ao mesmo tempo, conforme mostra a figura 3.
É claro que neste caso, temos uma limitação, pois não será possível acionar os LEDs 1, 2 e 4 ao mesmo tempo sem acender o 3. No entanto, podemos usar artifícios para poder acender as combinações “proibidas” o que consiste em fazer uma multiplexação no tempo.
O que fazemos então é criar ciclos de acendimento com as posições permitidas e intercalá-los com as posições conflitantes. Por exemplo, podemos acender os LEDs 1 e 2 num ciclo e o 4 em outro e assim o LED 3 não acende, conforme mostra a figura 4.
Se o tempo de comutação for muito curto (inferior a 0,05 segundos) os olhos não percebem a mudança e todos aparecem acesos ao mesmo tempo. No caso de se usar um microcontrolador isso pode ser obtido facilmente por software.
O que vimos como exemplo levou em conta apenas 4 LEDs, mas podemos fazer isso com muito mais LEDs, por exemplo, numa grande matriz de efeitos para vestíveis, painéis ou decoração com 10 x 10 LEDs, obtendo assim 100 combinações de LEDs únicos e muito mais com acionamentos mais complexos de 2, 3 ou mais LEDs simultâneos.
Na figura 5 temos exemplo de uma dessas matrizes.
Nessa matriz os LEDs correm, ficando sempre apenas um aceso, pois o 4017 é um contador 1 de 10, numa velocidade que dependerá do ajuste do 555.
Fazendo o acionamento com circuitos lógicos
Usando circuitos digitais simples CMOS ou TTL podemos controlar matrizes de LEDs sem a necessidade de microcontroladores. Já publicamos dezenas de projetos fazendo isso.
Uma forma simples é usando os circuitos integrados 4017 e também TTL como o 74 42para obter o acionamento 1 de 10 em cada saída e com isso 100 posições de acionamento numa matriz de 100 LEDs, conforme mostra a figura 6.
Observe que podem ser usados inversores para as colunas, pois elas devem habilitar o acionamento do LED correspondente quando estiverem no nível baixo. No entanto, este circuito trabalha com saídas 1 de 10, o que quer dizer que que apenas um LED será acionado de cada vez, cujo endereço será dado pela linha e coluna habilitadas. Neste caso o acionamento resultante deve ser previsto.
Este circuito tem uma compatibilidade com os microcontroladores, pois podemos fazer o acionamento pela contagem de pulsos. No exemplo dado usamos dois osciladores com o 555. No entanto, podemos usar apenas duas saídas de um microcontrolador para escolher um LED que será acionado. Isso pode ser feito, por exemplo se enviarmos 5 pulsos para as colunas e 7 pulsos para as linhas.
Com isso o LED que está no cruzamento da 5ª coluna com a 7ª linha será o acionado. É claro que haverá um corrimento dos LEDs no acionamento até que o efeito pare no LED selecionado. Os circuitos integrado 4017 possuem uma entrada de habilitação que podem ser usadas para que o LEDs escolhido só acenda no final da contagem.
O equivalente TTL para este circuito é o 7442. A vantagem deste circuito é que ele pode ser excitado de forma paralela por um microcontrolador, conforme mostra a figura 7.
Mas, podemos fazer o acionamento simultâneo com outros circuitos integrados, por exemplo, da série TTL que podem ser encontrados nas versões de 5 V e com tensões mais baixas para operação com microcontroladores.
Usando Microcontroladores
Uma forma mais interessante de se fazer o acionamento é através de uma codificação digital obtida de 8 saídas de um microcontrolador. Podemos usar 4 saídas para as linhas e 4 saídas para as colunas.
Assim, podemos excitar com o 7442, por exemplo, 10 colunas ou linhas usando apenas 4 saídas de um microcontrolador, conforme mostra a figura da figura 8.
A codificação pode ser feita através de um programa, lembrando que neste caso precisamos dos inversores para um acionamento mais simples, mas ele não é obrigatório. Basta lembrar que para as colunas o acionamento se obtém com o 0 e não com 1.
Este tipo de acionamento também pode ser feito com circuitos integrados CMOS equivalentes como o 4028, mostrado na figura 9.
É claro que devemos ter em conta as correntes que podem ser obtidas nas saídas. Um sistema de acionamento microcontrolado usando este circuito integrado é mostrado na figura 10.
Mas, e se desejarmos usar esta técnica para acionar cargas de maior potência, por exemplo, triacs, transistores ou SCRs a partir dos sinais de um microcontrolador. Uma saída simples consiste no emprego de acopladores ópticos, conforme mostra a figura 11.
A programação do microcontrolador pode então ser usada para acionar cargas maiores, com a vantagem o isolamento, usado esta técnica.
Veja que, com apenas 8 portas de um microcontrolador podemos controlar até 100 cargas, e de forma combinada se usarmos um sistema de multiplexação. Com isso, em microcontroladores com menos saídas, podemos ter uma considerável ampliação de suas capacidades.
O Software
Para fazer seu programa de acionamento, basta endereçar os comando para as portas usadas segundo a matriz que elas formam. Na internet existem muitos sketches prontos, o que facilita muito o programador não muito dado a trabalhar com uma tabela de dados.
Mesmo porque, se já existe pronto, por que ter trabalho em fazer tudo de novo? Podemos pegar o sketch pronto e adaptá-lo, se necessário, à nossa finalidade.
Vamos dar como exemplo o que encontramos pronto e que pode ser usado para seus projetos e que é dado a seguir que serve para uma matriz de LEDs 8 x 8 que mostra as letras do alfabeto.
#define ROW_1 2
#define ROW_2 3
#define ROW_3 4
#define ROW_4 5
#define ROW_5 6
#define ROW_6 7
#define ROW_7 8
#define ROW_8 9
#define COL_1 10
#define COL_2 11
#define COL_3 12
#define COL_4 13
#define COL_5 A0
#define COL_6 A1
#define COL_7 A2
#define COL_8 A3
const byte rows[] = {
ROW_1, ROW_2, ROW_3, ROW_4, ROW_5, ROW_6, ROW_7, ROW_8
};
const byte col[] = {
COL_1,COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8
};
// The display buffer
// It's prefilled with a smiling face (1 = ON, 0 = OFF)
byte ALL[] = {B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111};
byte EX[] = {B00000000,B00010000,B00010000,B00010000,B00010000,B00000000,B00010000,B00000000};
byte A[] = { B00000000,B00111100,B01100110,B01100110,B01111110,B01100110,B01100110,B01100110};
byte B[] = {B01111000,B01001000,B01001000,B01110000,B01001000,B01000100,B01000100,B01111100};
byte C[] = {B00000000,B00011110,B00100000,B01000000,B01000000,B01000000,B00100000,B00011110};
byte D[] = {B00000000,B00111000,B00100100,B00100010,B00100010,B00100100,B00111000,B00000000};
byte E[] = {B00000000,B00111100,B00100000,B00111000,B00100000,B00100000,B00111100,B00000000};
byte F[] = {B00000000,B00111100,B00100000,B00111000,B00100000,B00100000,B00100000,B00000000};
byte G[] = {B00000000,B00111110,B00100000,B00100000,B00101110,B00100010,B00111110,B00000000};
byte H[] = {B00000000,B00100100,B00100100,B00111100,B00100100,B00100100,B00100100,B00000000};
byte I[] = {B00000000,B00111000,B00010000,B00010000,B00010000,B00010000,B00111000,B00000000};
byte J[] = {B00000000,B00011100,B00001000,B00001000,B00001000,B00101000,B00111000,B00000000};
byte K[] = {B00000000,B00100100,B00101000,B00110000,B00101000,B00100100,B00100100,B00000000};
byte L[] = {B00000000,B00100000,B00100000,B00100000,B00100000,B00100000,B00111100,B00000000};
byte M[] = {B00000000,B00000000,B01000100,B10101010,B10010010,B10000010,B10000010,B00000000};
byte N[] = {B00000000,B00100010,B00110010,B00101010,B00100110,B00100010,B00000000,B00000000};
byte O[] = {B00000000,B00111100,B01000010,B01000010,B01000010,B01000010,B00111100,B00000000};
byte P[] = {B00000000,B00111000,B00100100,B00100100,B00111000,B00100000,B00100000,B00000000};
byte Q[] = {B00000000,B00111100,B01000010,B01000010,B01000010,B01000110,B00111110,B00000001};
byte R[] = {B00000000,B00111000,B00100100,B00100100,B00111000,B00100100,B00100100,B00000000};
byte S[] = {B00000000,B00111100,B00100000,B00111100,B00000100,B00000100,B00111100,B00000000};
byte T[] = {B00000000,B01111100,B00010000,B00010000,B00010000,B00010000,B00010000,B00000000};
byte U[] = {B00000000,B01000010,B01000010,B01000010,B01000010,B00100100,B00011000,B00000000};
byte V[] = {B00000000,B00100010,B00100010,B00100010,B00010100,B00010100,B00001000,B00000000};
byte W[] = {B00000000,B10000010,B10010010,B01010100,B01010100,B00101000,B00000000,B00000000};
byte X[] = {B00000000,B01000010,B00100100,B00011000,B00011000,B00100100,B01000010,B00000000};
byte Y[] = {B00000000,B01000100,B00101000,B00010000,B00010000,B00010000,B00010000,B00000000};
byte Z[] = {B00000000,B00111100,B00000100,B00001000,B00010000,B00100000,B00111100,B00000000};
float timeCount = 0;
void setup()
{
// Open serial port
Serial.begin(9600);
// Set all used pins to OUTPUT
// This is very important! If the pins are set to input
// the display will be very dim.
for (byte i = 2; i <= 13; i++)
pinMode(i, OUTPUT);
pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);
}
void loop() {
// This could be rewritten to not use a delay, which would make it appear brighter
delay(5);
timeCount += 1;
if(timeCount < 20)
{
drawScreen(A);
}
else if (timeCount < 40)
{
drawScreen(R);
}
else if (timeCount < 60)
{
drawScreen(D);
}
else if (timeCount < 80)
{
drawScreen(U);
}
else if (timeCount < 100)
{
drawScreen(I);
}
else if (timeCount < 120)
{
drawScreen(N);
}
else if (timeCount < 140) {
drawScreen(O);
}
else if (timeCount < 160)
{
drawScreen(ALL);
}
else if (timeCount < 180)
{
drawScreen(ALL);
}
else {
// back to the start
timeCount = 0;
}
}
void drawScreen(byte buffer2[])
{
// Turn on each row in series
for (byte i = 0; i < 8; i++) // count next row
{
digitalWrite(rows[i], HIGH); //initiate whole row
for (byte a = 0; a < 8; a++) // count next row
{
// if You set (~buffer2[i] >> a) then You will have positive
digitalWrite(col[a], (buffer2[i] >> a) & 0x01); // initiate whole column
delayMicroseconds(100); // uncoment deley for diferent speed of display
//delayMicroseconds(1000);
//delay(10);
//delay(100);
digitalWrite(col[a], 1); // reset whole column
}
digitalWrite(rows[i], LOW); // reset whole row
// otherwise last row will intersect with next row
}
}
//
/* this is siplest resemplation how for loop is working with each row.
digitalWrite(COL_1, (~b >> 0) & 0x01); // Get the 1st bit: 10000000
digitalWrite(COL_2, (~b >> 1) & 0x01); // Get the 2nd bit: 01000000
digitalWrite(COL_3, (~b >> 2) & 0x01); // Get the 3rd bit: 00100000
digitalWrite(COL_4, (~b >> 3) & 0x01); // Get the 4th bit: 00010000
digitalWrite(COL_5, (~b >> 4) & 0x01); // Get the 5th bit: 00001000
digitalWrite(COL_6, (~b >> 5) & 0x01); // Get the 6th bit: 00000100
digitalWrite(COL_7, (~b >> 6) & 0x01); // Get the 7th bit: 00000010
digitalWrite(COL_8, (~b >> 7) & 0x01); // Get the 8th bit: 00000001
}*/
A figura 12 mostra como fazer as conexões no Arduino. As conexões são dadas para uma matriz comercial, mas nada impede que o leitor coloca ele mesmo os LEDs numa placa universal seguindo a organização de linhas e colunas.
Mais projetos
Explicando o modo como podemos usar microcontroladores e circuitos digitais em controles matriciais, expandindo assim suas capacidades pelo uso de menor número de portas, o leitor pode ir além.
Muitas ideias para TCCs podem vir à tona a partir do que ensinamos. Um projeto interessante que publicamos, mas usando circuitos discretos é de um mini osciloscópio digital com LEDs, mostrado na figura 13.
Evidentemente, pelas características do circuito, ele serve mais como curiosidade operando com sinais de áudio e mesmo assim com uma baixa definição.
O leitor pode modificar este circuito adaptando-o para funcionar como um Arduino e assim demonstrar as capacidades de operação de microcontroladores como DSPs (Processadores Digitais de Sinais) o que pode ser muito interessante num trabalho.
Conclusão
Projetar com o Arduino e outros microcontroladores pode ser simples e interessante quando se conhece eletrônica digital. Veja que neste projeto trabalhamos com 16 saídas para um acionamento matricial para 64 LEDs.
Com o uso de integrados adicionais, o que levamos aos leitores neste artigo, poderíamos fazer o mesmo com metade das saídas, colocando 3 circuitos integrados adicionais na matriz. Isso será muito muito importante num projeto mais complexo, pois estaríamos liberando outras para outras funções, ou mesmo para acionar um segundo display.
Isso levaria não apenas a termos as letras do alfabeto individualmente, mas combinadas duas a duas multiplicando assim as possibilidades de combinações. É por esse motivo que continuamos a achar muito importante o ensino da eletrônica digital antes do microcontrolador.