sexta-feira, 17 de agosto de 2018

Arduino I/O

Se você estiver com um Arduino em mãos verá que ele possui vários pinos, explicarei o funcionamento de todos eles. Primeiro iremos focar nos pinos de I/O (Entrada ou saídas de dados) digitais, que são os pinos numerados de 0 a 13. Alguns possuem uma identificação (~) antes do número, não se preocupe, explicarei mais tarde o que esse símbolo significa. (spoiler: PWM).

Programar pino como saída


Os pinos são digitais, pois só possuem dois níveis lógicos, HIGH ou LOW, ligado ou desligado, true ou false, 5 V ou 0 V. Dessa forma, podemos ligar ou desligar uma carga, e é o que faremos agora. O Arduino possui um LED em sua placa ligado ao pino 13, iremos ligar e desligar esse LED em um intervalo de 1s, repetidamente.

Conecte seu Arduino ao computador e upload o seguinte código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
uint8_t ledPin = 13;

void setup() 
{
  pinMode(ledPin, OUTPUT);
}

void loop() 
{
  digitalWrite(ledPin, HIGH);   
  delay(1000);                       
  digitalWrite(ledPin, LOW);    
  delay(1000);                       
}

Depois que o upload estiver completo, você observará um pequeno LED, provavelmente um LED vermelho, acender e apagar em um intervalo de 1s. Esse é o “Hello World” no mundo dos microcontroladores, bem vindo a esse mundo. 

Seria possível ainda conectar um LED (da cor que você preferir) ao pino 13, nesse caso, seria necessário o uso de um resistor ligado em serie com o LED para limitar a corrente fornecida pelo Arduino, lembre-se do limite de 40 mA. Para a maioria desses LEDs comuns, um resistor de 220 Ω é suficiente. O LED possui polaridade, seu terminal positivo deve ser ligado ao pino 13 e o negativo ao resistor, o resistor não possui polaridade, o terminal que sobra do resistor deve ser ligado ao pino GND (terra) do Arduino. Como pode ser visto na figura a seguir:

Figura 1. Ligação de um LED a pino 13 do Arduino

Espero que você esteja tão impressionado quanto eu fiquei quando fiz isso pela primeira vez. Mas vamos à explicitação do código.

Linha 1, ocorre uma declaração de uma variável do tipo uint8_t, que significa um inteiro sem sinal de 8 bits, com 8 bits sem sinal é possível representar números de 0 a 255, o que é mais que suficiente para representar os pinos do Arduino. A palavra ledPin é o nome escolhido para a variável, é importante escolher um nome significativo, o símbolo de (=) é responsável por atribuir um número a essa variável, no caso, o número 13 que é o pino onde o LED está ligado.

Linha 3, da linha 3 até a linha 6, temos uma função chamada setup (Algo como configuração) do tipo void (vazio), o tipo da função representa o valor que será retornado por ela no fim de sua execução, uma função tipo void não retorna nenhum valor. Todo código para Arduino terá essa função, ela é responsável por configurações iniciais.

Linha 5, ocorre a chamada de uma função, pinMode, que configura como um pino será usado (Entrada ou saída) e recebe dois parâmetros, um número ou variável que representa um dos pinos do Arduino e a configuração que será usada, no caso, OUTPUT, pois queremos enviar dados para o pino.

Linha 8, da linha 8 até a 16, temos uma função chamada loop (laço) do tipo void. Como a função setup, todo código para Arduino possuirá essa função, ela é responsável por criar uma repetição infinita, assim, todo código que estiver dentro da função loop será executado repetidamente.

Linha 10, ocorre a chamada de uma função, digitalWrite (Write = escrever), que envia uma informação para um determinado pino (5 V ou 0 V) e recebe dois parâmetros, o pino que será modificado e o nível lógico (HIGH ou LOW).

Linha 11, ocorre a chamada de uma função, delay (atraso), que recebe um parâmetro que é o tempo (em ms) que a execução das instruções serão pausadas. Depois desse intervalo a função digitalWrite é chamada para modificar o nível lógico do pino, mas dessa vez para LOW, em seguida mais um delay. Após a execução do último delay, a sequência de execução voltará para a linha 10.

Como esse foi o primeiro código nesse tutorial, resolvi dá uma explicação bem detalhada, os próximos códigos serão explicados de forma mais sucinta com o auxilio de comentários nas linhas de código.

Programar pino como entrada


Fazer um LED piscar é legal, mas e se você quiser mudar o estado de um LED com auxílio de um botão, seria possível? Sim seria :D, e é o que faremos a seguir. Para isso, usaremos outro pino para ler um dado de um botão. Para ler um nível lógico de um botão usaremos um resistor de pullup de 10 kΩ ligado ao pino de 5 V. Como pode ser visto na imagem a seguir:

Figura 2. Ligação de um LED e um botão ao Arduino
Conecte seu Arduino ao computador e upload o seguinte código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
uint8_t ledPin = 13;        // LED no pino 13
uint8_t buttonPin = 7;      // Botão no pino 7
bool state = 0;             // Variavel para o estado do LED, bool = 1 bit

void setup() 
{
  pinMode(ledPin, OUTPUT);   // Pino do LED como saida
  pinMode(buttonPin, INPUT); // Pino do botão como entrada
}

void loop() 
{
  if(!digitalRead(buttonPin))    // A função if testa se o botão foi pressionado
  {
    state = !state;              // Inverte o valor de state, 0 -> 1 ou 1 -> 0
    digitalWrite(ledPin, state); // LED = state, ligado ou desligado
    delay(500);                  // delay de 500ms (0.5s)
  }               
}

Ao pressionar o botão, o LED mudará de estado, se estiver acesso irá apagar e se estiver apagado irá acender. O pino 7, com a função digitalRead (read = ler), estará sempre lendo um nível lógico alto, mas quando apertamos o botão será lido um nível lógico baixo. Na linha 13 usamos a função if (mais informação aqui) para testar se o botão foi pressionado, caso isso seja verdade, os comandos entre colchetes serão executados, invertendo o estado do LED.

O uso do delay permite que se passe um tempo antes do próximo teste do botão, se esse tempo for muito pequeno, o LED mudará de estado varias vezes enquanto o botão for pressionado. A desvantagem do delay é que como código é executado sequencialmente se pressionamos o botão exatamente durante o delay, não será possível ocorrer o teste e o LED não mudará seu estado. Essa limitação pode ser superada usando-se um assunto avançado de microcontroladores, que será explicado mais a frente (spoiler: Interrupção).

Como pullup é muito usado, o Arduino (no caso, o ATmega328P) possui um internamente em cada pino, para usa-lo no pino desejado basta modificar a linha 8 do código, com o seguinte comando:

pinMode(buttonPin, INPUT_PULLUP);

Retire o resistor de pullup, o fio ligado ao pino de 5 V e upload o novo código. Se você seguiu todos os passos certo, o funcionamento deve ser o mesmo do anterior.

AVR comandos


A função digitalWrite é útil quando queremos usar apenas um pino, mas se quisermos, por exemplo que o botão do exemplo anterior acione mais de um LED, teríamos que escrever a função digitalWrite varias vezes de acordo com o numero de LEDs. A linguagem usada na programação do Arduino foi feita para facilitar a programação do microcontrolador, mas é possível programá-lo da forma “Hardcore”, que seria usando a funções usadas na programação de microcontroladores AVR, como o ATmega328P.

Para isso é necessário saber como os pinos do ATmega328P estão ligados no Arduino. Como pode ser visto na imagem a seguir:

Figura 3. AVR I/O e Arduino Pinos

Pela imagem, vemos, por exemplo, que os pinos do Arduino de 13 a 7 estão ligados ao PORTB. PORTB é um registrador de 8 bits que agrupa 8 pinos do microcontrolador, PB0-PB7. Além do PORTB, existem o PORTD (8 bits) e o PORTC (7 bits). Dessa forma, podemos programar o Arduino (ATmega328P) utilizando os registradores. Para o primeiro exemplo, onde o LED no pino 13 foi ligado e apagado a cada 1s, poderíamos utilizar o seguinte código:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <avr/io.h> // Importação de biblioteca 

void setup() 
{
  DDRB = 0b00100000; // Configura PB5 como saída, PB5 = 13 (Arduino)
}

void loop() 
{
  PORTB = 0b00100000;  // Ativa PB5, 0b = numero em binario
  delay(1000);
  PORTB = 0b00000000;  // Desativa PB5
  delay(1000);             
}

O Arduino (Placa + IDE) permite a programação do AVR (ATmega328P) de forma bem mais fácil. Nesse momento estamos utilizando comandos do Arduino junto com comandos para AVR.

Na linha 1, ocorre a importação de uma biblioteca, o uso dela é necessário se você estiver utilizando outro IDE que não seja o do Arduino. Linha 5, DDRB seria o equivalente a pinMode, mas o DDRB permite a configuração de todos os pinos do PORTB, no caso, utilizamos um numero em binário para isso, 0 temos entrada e 1 saída.

Linha 10 e 12, temos PORTB que seria o equivalente ao digitalWrite, mas o PORTB permite a escrita de todos os pinos do PORTB, no caso, utilizamos um numero em binário para isso, 0 temos desativo e 1 ativo.

O uso do PORTB permitiu o acesso a 8 pinos, mas utilizamos apenas 1. No próximo exemplo utilizaremos 4 pinos ligados a 4 LEDS, onde acenderemos cada um sequencialmente e ao apertamos um botão, ligado a outro pino, o sentido será mudado.

Ligue os LEDs e o botão no Arduino de acordo com a imagem a seguir:

Figura 4. LEDs sequenciais

Conecte o Arduino ao seu computador e upload o seguinte códido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
uint8_t pinButton = 4; // Botão no pino
bool seq = 0;         // Sentido 
uint16_t delay_ms = 150; // Delay entre LEDs, 16 bits -> max = 65535     

void setup() 
{
  DDRB = 0b00111100; // Configura PB5-PB2 como saída, (13,12,11,10) (Arduino)
  pinMode(pinButton, INPUT_PULLUP); // Ativa pullup do pino 4
}

void loop() 
{
  if(!digitalRead(pinButton)) // Ler botão
  {
    seq = !seq; // Muda o sentido se o botão foi pressinado
  }

  if(seq) // Se seq = 1, segue esse sentindo
  {
    PORTB = 0b00100000;  // Liga LED 13
    delay(delay_ms);
    PORTB = 0b00010000;  // Liga LED 12
    delay(delay_ms);
    PORTB = 0b00001000;  // Liga LED 11
    delay(delay_ms);
    PORTB = 0b00000100;  // Liga LED 10
    delay(delay_ms);
  }

  else  // Se seq = 0, segue esse sentido
  {
    PORTB = 0b00000100;  // Liga LED 10
    delay(delay_ms);
    PORTB = 0b00001000;  // Liga LED 11
    delay(delay_ms);
    PORTB = 0b00010000;  // Liga LED 12
    delay(delay_ms);
    PORTB = 0b00100000;  // Liga LED 13
    delay(delay_ms);
  }
}

Linha 30 apresenta uma novidade, que é o comando else, o comando else faz parte da função if, seria seu complementar. Assim, caso o teste realizado pelo o if for falso, então, obrigatoriamente, os comando entre chaves do else serão executados. Além, do else, existe o else if, que faz um teste antes da execução.

Se você seguiu todos os passos corretamente, os LEDs acenderão um por um, sequencialmente, do pino 10 ao 13 e depois voltando ao pino 10. No momento em que você apertar o botão, a sequencia mudará de sentido, do pino 13 ao pino 10 e depois voltando ao pino 13. Lembre-se que a sequencia só mudará se no momento do teste (if) o botão estiver pressionado. Logo no inicio dó código, existe uma variável que controla o valor do delay, teste outros valores e veja o que acontece.

A intenção foi só mostrar um pouco do que é possível fazer usando comandos para AVR, mas como o objetivo é ensinar Arduino, focarei mais na programação para Arduino. Agora, tente imaginar o tamanho do código usando-se o digitalWrire e pinMode no lugar do PORTB e DDRB.

Enfim, chegamos ao final dessa parte, parabéns por ter chegado até aqui. Na próxima parte serão abortados os assuntos conversor AD e PWM, até lá.

<< Arduino Introdução                                                                                  Arduino PWM e ADC >>  

Um comentário: