Aprendendo Arduino — Parte 2 — visualizando bits

meiobit

Por muito tempo a eletrônica como um todo foi essencialmente analógica. O mundo é analógico. Existem infinitas variações entre o 1 e o 11 no volume do seu aparelho de som, se for dos antigos e usar potenciômetros. Você não salta do volume 1 para o 2, pode variar de acordo com a sua coordenação motora final. 

Já no mundo digital, binário, as coisas são ou não são. Uma porta por exemplo: ela pode estar aberta ou fechada, isso é um conceito binário. No mundo real ela pode estar entreaberta, ou se fechando, ou se abrindo.

O mundo analógico é muito mais rico, mas muito mais difícil de quantificar. Por isso os computadores chamados “analógicos” eram raros complicados e se dedicavam a tarefas específicas. Um bom exemplo de computador analógico é o Mecanismo de Antikythera, construído em 150 AC, na Grécia. Era composto de 37 engrenagens, calculava posições de corpos celestes e previa eclipses. Não, não rodava Crysis.

antikythera_mechanism_remains

Ironicamente a eletrônica analógica não tornou mais fácil a construção de computadores analógicos, computadores só se tornaram realmente úteis e versáteis com o advento da eletrônica digital, mas eu vou contar um segredo: a Eletrônica Digital não existe.

Este é um sinal analógico:

analog-signal

Ele representa uma variação de amplitude, na vertical, versus o tempo, na horizontal. Pode ser volume sonoro, voltagem tensão, intensidade de luz, não importa. há infinitas variações entre a intensidade e o tempo entre os picos e vales do sinal.

Este é um sinal digital:

51c495ebce395f1b5a000000

Presença de voltagem tensão representa um, ausência representa zero, é assim que computadores falam.

Só que não. Você nunca terá zero volts exatos, nunca terá cinco volts exatos e precisa de mecanismos para garantir o intervalo entre os pulsos, que também não sobem de zero a 5 instantaneamente. Este é um sinal digital real:

imagen_4

É definido um valor de voltagem tensão positiva representando bits 1 e o mesmo valor, mas de tensão negativa representando bits 0. Note que os valores não são absolutamente estáveis, mas não importa, não existe meio bit (ok existe e chegaremos lá). Dependendo do nível de proteção a ruído que seu circuito tenha, esse valor pode ser arbitrariamente baixo e ainda assim considerado um um, mas em essência ele é uma onda analógica ainda.

Internamente todos os componentes digitais são compostos de componentes analógicos. Capacitores, resistores, transístores, todos eles só funcionam com sinais analógicos, o próprio conceito de digital é uma abstração. Não existe um componente digital mágico, e isso vai até a mais minúscula parte de um chip. Uma célula de memória SRAM para guardar um bit utiliza quatro transístores e dois resistores. Ou seja: a imagem de abertura deste artigo é… meio bit.

Aqui um circuito básico usando dois transístores e quatro resistores para guardar um bit:


Carlos Cardoso — Um bit

Circuitos assim são a base dos componentes digitais, pois permitem que a gente utilize matemática binária, consolidada na chamada Lógica Booleana, criada em 1847 por George Boole, autor de A Análise Matemática da Lógica, e que obviamente tinha aquário em casa.

Os números binários são os números de sempre, mas representados em outra base matemática. Se você não for um famoso ex-presidente, provavelmente 10 dez dedos na mão, então não é difícil deduzir por quê quase toda cultura usa base dez.

Computadores não têm dedos, e têm excelente memória, então para eles não faz diferença que 187.958.933 em binário seja 01011001101000000011010010101. Na verdade é até mais fácil, pois suas células de memória individuais só precisam de dois estados, zero ou um.

O Arduino é um processador de oito bits, o que significa que ele manipula um byte por vez. Um byte por ter valores de 00000000 a 11111111, ou zero a 255 em decimal. E não se assuste, não há mistério em trabalhar com números binários. Primeiro, pode usar calculadora. Segundo, é conta primária.

Para converter de Binário para Decimal:

Digamos que você tem o número 11011101 e quer saber quando é em decimal. Atribua, da direita para a esquerda, o valor de 2 elevado à potência da posição do número, assim:

2726252423222120

Em decimal:

128,64,32,16,8,4,2,1

Agora é só somar os valores, se o bit em questão for 1.

11011101

1 → 128

1 → 64

0 → não soma

1 → 16

1 → 8

1 → 4

0 → não soma

1 → 1

128 + 64 + 16 + 8 + 4 + 1 = 221

E para converter de binário pra decimal? Mais fácil ainda.

Pegue o valor inicial, digamos, 221. Divida por dois, trabalhando com inteiros.

221/2 = 110, resta 1

Agora repita:

110/2, dá 55, resta 0

55/2, dá 27, resta 1

27/2  dá 13, resta 1

13/2 dá 6, resta 1

6/2 dá 3, resta 0

3/2 dá 1, resta 1

1/1 dá 1, resta 1

No um você pára. Agora pegue os restos, de baixo pra cima:

11011101 o nosso 221, em binário.

Agora imagine se em vez de você fazendo essas contas, fosse o processador. Se você entregar os valores mastigados em binário, ele tem muito menos com que se preocupar, por isso a programação de baixo nível trabalha direto com binário.

Quando se trabalha com Arduino mesmo que você não perceba, está trabalhando com código binário, bem próximo do “metal”, como costumamos falar. Vejamos o exemplo clássico do LED piscante, Hello World do Arduino. O circuito é o mais simples possível, qualquer idiota consigo montar:

blink

São dois fios um LED e um resistor de 1 kΩ, senão os 5 V vão transformar seu LED em um episódio do ElectroBOOM.

O negativo você espeta no terra, ou GND do Arduino. O positivo você espeta no pino 13 do Arduino, e a outra ponta no resistor, que por sua vez está conectado ao LED. Na IDE do Arduino você busca por exemplos / basic / blink, carrega o programa, manda executar. O LED onboard do Arduino irá piscar, e se tudo der certo você verá o LED em sua protoboard piscando também. Se não piscar, você é um idiota como eu e colocou o LED invertido. Desinverta-o.

O programa é simplíssimo:

void setup() {
 // initialize digital pin LED_BUILTIN as an output.
 pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
 digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
 delay(1000); // wait for a second
 digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
 delay(1000); // wait for a second
}

O programa-padrão no Arduino envolve duas seções príncipais: uma de setup, onde você prepara o ambiente, e uma de loop, que se repete infinitamente. No nosso exemplo acima a seção de setup tem apenas um comando:

 pinMode(LED_BUILTIN, OUTPUT);

Ele define que o pino de entrada e saída LED_BUILTIN será de saída, OUTPUT. No caso LED_BUILTIN é apenas um mnemônico, um valor pré-definido que internamente é reconhecido como o número 13. Se você escrever:

 pinMode(13, OUTPUT);

O resultado será o mesmo.

Na próxima linha significativa, dentro do loop, temos:

 digitalWrite(LED_BUILTIN, HIGH);

Esse comando indica ao Arduino para aplicar ao pino LED_BUILTIN o valor digital 1, ou HIGH.

Embora seja tecnicamente um sinal digital, nem o mais esperto dos LEDs entende isso, ou se importa. Ele vê corrente na portinha, e se acende. É uma interação entre os mundos digitais e analógicos, a mais simples de todas.

Em seguida temos o comando:

delay(1000);

É uma pausa, manda o processador esperar 1.000 milissegundos.

O comando:

digitalWrite(LED_BUILTIN, LOW);

Faz o oposto, aplica ao pino selecionado o valor digital LOW, que pode ser zero ou negativo mas meu multímetro está sem bateria e estou com preguiça de pesquisar como o Arduino protocoliza isso.

Outra pausa de um segundo, e o loop se reinicia.

Beleza, vamos complicar. E se eu quisesse usar 3 LEDs ao mesmo tempo, assim:

Vamos ligar os leds nos pinos 8, 9 e 10 do Arduino, e definir no nosso programa esses pinos como saída:

void setup() {
 // initialize digital pin LED_BUILTIN as an output.
 pinMode(8, OUTPUT);
 pinMode(9, OUTPUT);
 pinMode(10, OUTPUT);

}

Beleza, vamos fazer os três piscarem. usando o código lá de cima, o loop ficaria assim:

void loop() {
 digitalWrite(8, HIGH); 
 digitalWrite(9, HIGH);
 digitalWrite(10, HIGH);
 delay(1000); 
 digitalWrite(8, LOW);
 digitalWrite(9, LOW);
 digitalWrite(10, LOW);
 delay(1000); 

}

Não me parece muito eficiente, e olha que sou um programador porco. Deve ter um meio de acessar esses pinos ao mesmo tempo. E tem!

Lembre-se: estamos trabalhando muito próximos do metal, o ATmega328 sente o bafo na nuca. Não há complexidade desnecessária, e os vários conjuntos de pinos do Arduino estão mapeados diretamente com registradores da CPU.

Um registrador nada mais é que uma posição especial de memória, acessada diretamente pelo hardware e por isso mesmo muito, muito eficiente.

O Arduino possui registradores associados com os bancos de pinos. No caso nos interessa o registrador chamado PORTB.

Ele possui um registrado auxiliar chamado DDRB, cada um de seus 8 bits define se o pino correspondente será INPUT ou OUTPUT.

Lembra da sequência de comandos:

 pinMode(8, OUTPUT);
 pinMode(9, OUTPUT);
 pinMode(10, OUTPUT);

Em vez dela podemos usar:

DDRB = B00000111;

0 é INPUT, 1 é OUTPUT.

(você deve ter notado que não existem pinos 14 e 15. Eles são conectados ao cristal oscilador e não são usáveis por nós)

Agora a sequência:

digitalWrite(8, HIGH);
digitalWrite(9, HIGH);
digitalWrite(10, HIGH);

Como isso ficaria usando um registrador? Fácil:

PORTB=B00000111;

E apagar os LEDs seria…

PORTB=B00000000;

O programa inteiro agora é…

void setup() {

DDRB = B00000111;

}

void loop() {

PORTB=B00000111;
delay(1000);
PORTB=B00000000;
delay(1000);
}


Carlos Cardoso — Teste de LEDS

Vamos ser MAIS ambiciosos? Temos 3 bits em sequência associados a 3 LEDs. Vamos construir então um contador binário, indo de 000 a 111, ou 7 para macacos pelados que inventaram matemática com base em seus dedos. Você não, presidente.

Primeiro, ANTES do setup definimos uma variável global. Precisamos de um inteiro. No começo do programa, criamos:

int jorge;

Agora no setup determinamos que os pinos 8, 9 e 10 serão OUTPUT:

void setup() {

 DDRB = B00000111;

}

No loop vamos usar uma extrutura de FOR/NEXT, que executa um loop um determinado número de vezes:

void loop() {
 
 for (jorge = B000; jorge <= B111; jorge++) {
  PORTB = jorge;
  delay(500); 
 }
}

A linha diz para executar o loop começando com a variável jorge com o valor binário de 000, executar enquanto ela for menor ou igual a 111, e a cada iteração, adicionar 1 ao valor de jorge. Em linguagem C o operador ++ significa incremento unitário.

O próximo comando atribui ao registrador PORTB o valor de jorge.

byte

O programa usou 11 bytes de RAM e 644 bytes de FLASH para criar um contador binário, acessar portas de saída e gerar um display. Vai, faz isso em Java…


Carlos Cardoso — Contador binário usando 11 bytes de RAM

Leia também:

Relacionados: , , , , , , ,

Autor: Carlos Cardoso

Entusiasta de tecnologia, tiete de Sagan e Clarke, micreiro, hobbysta de eletrônica pré-pic, analista de sistemas e contínuo high-tech. Cardoso escreve sobre informática desde antes da Internet, tendo publicado mais de 10 livros cobrindo de PDAs e Flash até Linux. Divide seu tempo entre escrever para o MeioBIt e promover seus últimos best-sellers O Buraco da Beatriz e Calcinhas no Espaço.

Compartilhar

Aproveite nossos cupons de desconto:

Cupom de desconto Locaweb, Cupom de desconto HP, Cupom de desconto Descomplica, Cupom de desconto Nuuvem, Cupom de desconto CVC, Cupom de desconto Asus, Cupom de desconto World Tennis