Projeto 81 - Ligar e desligar um led por manipulação direta de portas do Arduino

Básico - Projeto 81

Leitura de entrada e saída utilizando registradores de portas do Arduino

Objetivo

Neste projeto vamos mostrar como utilizar registradores de portas DDR , PORT e PIN para fazer a leitura de entrada e saída do Arduino. Vamos, desta forma, exemplificar o conceito de DDR , PORT e PIN mostrando uma técnica muito eficiente para ligar e desligar um led ou qualquer componente eletrônico através de um botão ou de um sensor de entrada qualquer (no projeto vamos utilizar um botão e um sensor de toque).

Definições

Vantagens de se utilizar registradores:

  1. Velocidade de acesso, pois acionaremos a porta diretamente sem a necessidade de funções.
  2. Otimização de memória do microcontrolador.
  3. Eficiente quando precisamos ligar várias portas ao mesmo tempo.

Desvantagens de se utilizar registradores:

  1. Código de difícil entendimento.
  2. Criar códigos utilizando registradores não irão funcionar em todos os tipos e versões do Arduino. Cada microcontrolador possui estruturas de pinos digitais e analógicos diferentes.
  3. É muito mais fácil causar problemas de funcionamento não intencionais com acesso direto à porta.

Observações:

1. O conceito DDR, PORT e PIN utilizado neste projeto é específico para Arduino UNO R3 que utiliza o microcontrolador ATmega328. Outros microcontroladores podem ter configurações diferentes. Para saber mais, leia os artigos: Arduino - Manipulação Direta de Portas do blog do Renato Aloi e Port Registers - Documento de Referência Arduino.

2. O conceito de DDR e PORT também foi utilizado no Projeto 61 - Dado eletrônico com leds e uso de manipulação direta de portas do Arduino onde acionamos um conjunto de leds através da manipulação direta de portas do Arduino e no Projeto 60 - Dado eletrônico com Arduino (uso de manipulação direta de portas) onde controlamos uma matriz de leds com a manipulação dos registradores.

3. Para avançarmos um pouco mais na utilização de registradores de portas do Arduino, vamos precisar de um conhecimento básico sobre operadores bitwise. Para isso, recomendo que leiam o artigo Operadores Bitwise (Bit-a-Bit) do blog Diogo Matheus.

4. ATENÇÃO: Tome muito cuidado em utilizar o conceito de manipulação direta de portas. Qualquer erro pode danificar permanentemente a sua placa de Arduino.

Registradores de portas do Arduino Uno R3

Registradores de portas (DDR, PORT e PIN) permitem o acesso direto às portas do Arduino. Veja abaixo a imagem que mostra todas portas disponíveis do Arduino UNO:

Nos microcontroladores Arduino Uno, existem 3 canais de portas:

  • Porta B (portas digitais de 8 a 13)
  • Porta C (entradas analógicas)
  • Porta D (portas digitais de 0 a 7)

Para exemplificar melhor vamos utilizar o canal de portas D referente às portas digitais 0 a 7 do Arduino UNO. Microcontroladores de 8bits, como o Arduino UNO por exemplo, cada canal de portas possui 8 bits. Portanto, cada bit representa uma porta (ou pino) de cada canal de portas.

Cada porta do microcontrolador possui 3 registradores, sendo eles:

  • DDR que configura o sentido da porta se ela é saída (OUTPUT) ou entrada (INPUT). Substitui a função pinMode().
  • PORT que controla se a porta está no estado lógico alto (HIGH) ou baixo (LOW).
  • PIN que efetua a leitura das portas configuradas como INPUT pela função pinMode().

No nosso projeto vamos utilizar os registradores DDR e PORT conforme as instruções abaixo:

DDR (Data Direction Register - read/write)

  • No caso do registrador DDR, vamos acrescentar o canal de portas D, ficando portanto como DDRD (portas digitais de 0 a 7 no Arduino Uno).
  • Definimos para cada porta se é saída (OUTPUT), usando o 1 ou se é entrada (INPUT), usando o 0.
  • A ordem das portas é decrescente e vai de PD7 até PD0 e PB5 até PB0 (desconsidere o erro na imagem, leia PB5 no lugar de PB6)
  • Como é um número binário, devemos colocar o modificador "B" na frente, veja o exemplo a seguir:

Ex.:

DDRD = B11111110;  // configura portas 1 ate 7 como saídas,e a porta 0 como entrada

PORT (Data Register - read/write)

  • Como no registrador DDR, vamos acrescentar no PORT o canal de portas D, ficando portanto como PORTD (portas digitais de 0 a 7 do Arduino Uno).
  • Definimos para cada porta, se está no estado lógico HIGH, usamos 1 ou se está no estado lógico LOW, usamos 0.
  • A ordem das portas vai de PD7 até PD0.
  • Como é um número binário, devemos colocar o modificador "B" na frente, veja o exemplo a seguir:
PORTD = B10101000;  // registra valor HIGH nas portas digitais 7,5 e 3

Aplicação

Para fins didáticos e projetos exigem a utilização de códigos profissionais.

Componentes necessários

Referência

Componente

Quantidade

Imagem

Observação

Protoboard Protoboard 830 pontos 1 Resultado de imagem para protoboard 830v

No mínimo utilizar protoboard com 400 pontos

Jumpers Kit cabos ligação macho / macho 1  
Sensor de toque capacitivo Sensor touch TP223B 1

Tensão de Operação: 2 a 5V

Saída estado Alto (HIGH): 0,8V

Saída estado Baixo (LOW): 0,3V

Tempo de resposta: 220ms (LOW) e 60ms (HIGH)

(datasheet)

Led Difuso 5mm LEDs 5mm 1

1 LED alto brilho ou difuso de qualquer cor

Você poderá utilizar também LEDs de 3 mm na cor que desejar.

Push Button Push button 6X6X5mm 1  
Resistor

Resistor

 1

1 Resistor 150Ω

Se precisar usar outros valores, calcule o resistor apropriado para o led utilizado

Arduino UNO R3 Arduino UNO 1

Você poderá utilizar uma placa Arduino UNO original ou similar

Montagem do Circuito

Conecte os componentes no Protoboard como mostra a figura abaixo. Verifique cuidadosamente os cabos de ligação antes de ligar seu Arduino. Lembre-se que o Arduino deve estar totalmente desconectado da força enquanto você monta o circuito.

Atenção

1. Lembre-se que o LED tem polaridade: O terminal maior tem polaridade positiva e o lado do chanfro tem polaridade negativa.

1.1. Portanto, faça a conexão do Arduino no terminal positivo do led (anodo) e o GND no terminal negativo (catodo).

1.2. Para evitar danos ao led é necessário a inclusão de um resistor no circuito. Como o resistor é um limitador da corrente elétrica, ele poderá estar conectado no anodo (terminal maior) ou no catodo (terminal menor) do led, tanto faz.

2. Determinamos o valor do resistor através da tabela prática: Tabela prática de utilização de leds 3mm e 5mm. Entretanto, o mais correto é sempre verificar o datasheet do fabricante do LED para você ter os exatos valores de tensão e corrente do mesmo.

2.1. Valores utilizados para nossos projetos: LEDs difusos ou de alto brilho: Vermelho, Laranja e Amarelo: 150 Ω | Led Verde e Azul: 100 Ω

Atenção: Para evitar danos no componente, não utilize resistores menores que 100 Ω. Para saber mais como calcular o resistor, leia Leds ligados em série e em paralelo.

3.  Monte o botão (push button) sem o resistor, pois através da programação vamos habilitar o resistor pull-up interno do arduino. Desta forma, quando o botão estiver pressionado, o Arduino retornará "LOW" ou "0". Assista o vídeo Arduino: Botão e Resistor de Pull Up Interno


Obs.: Os resistores pull-up e pull-down garantem um nível lógico estável quando por exemplo uma tecla não está pressionada. Geralmente utiliza-se um resistor de 10KΩ para esse propósito. A seguir é exibida a ligação desses resistores no circuito para leitura de tecla do botão:

Entradas-e-Saídas-digitais-Arduino-pull-up-pull-down

Atenção: Observe que neste projeto não utilizaremos o resistor de 10K, pois vamos habilitar o resitor pull-up interno do Arduino através da programação, utilizando registradores de portas do Arduino Uno.

4. Monte o sensor touch TP223B conforme imagem abaixo. Desta forma, quando o sensor for tocado, o Arduino retornará "HIGH" ou "1".

4.1.1. No nosso exemplo conectamos o sensor no pino digital 2 do Arduino.


4. Veja abaixo como realizamos a montagem do nosso projeto utilizando um protoboard.

Utilizando registradores de portas - Arduino Uno R3

Para elucidarmos melhor a utilização dos registradores de portas do Arduino Uno R3 que utiliza microcontrolador ATmega328, vamos trabalhar com alguns exemplos simples. É importante que você reproduza os exemplos para um melhor entendimento.

OPERAÇÕES EM PORTAS

1. No exemplo abaixo, onde usamos funções do IDE do Arduino, temos uma aplicação bem simples: fazer com que um led, conectado no pino digital 3 do microcontrolador pisque a cada 0,5 segundos.

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

void loop() {
  digitalWrite(3, !digitalRead(3));
  delay(500);
}

1.1. Explicando o código resumidamente, o led irá acender e apagar a cada 0,5 segundos (500ms), pois a função digitalWrite() envia um sinal HIGH (5V) ou LOW (0V) para a porta do microcontrolador onde o led está conectado, que no nosso exemplo é o pino digital 3. A função digitalRead() lê qual sinal o pino 3 está recebendo, invertendo-o através do operador lógico NOT (!), ou seja, se se HIGH (5V) ele inverterá para LOW (0V) e vice e versa, criando criando o efeito "pisca-pisca". Referências: digitalWrite(), digitalRead() e operador NOT.

1.2. Observe que ao utilizarmos as funções digitalWrite() e digitalRead() como no exemplo acima, precisamos de 960 bytes (2%) de espaço de armazenamento para programas.

2. Agora vamos fazer o mesmo efeito utilizando registradores. Vamos enviar o sinal HIGH e LOW diretamente na porta digital 3 do microcontrolador.

void setup() {
  DDRD = B00001000; // define a porta 3 com saída  
}

void loop() {
    PORTD = B00001000; // envia sinal 5V - HIGH
    delay(500);
    PORTD = B00000000; // envia sinal 0V - LOW
    delay(500);
}

2.1. Como já vimos no início do tutorial, o registrador DDR configura o sentido da porta se ela é saída (OUTPUT) ou entrada (INPUT), sendo que:

2.1.1. O led deverá estar configurado como porta de saída (OUTPUT). Portanto, devemos definir a porta digital 3 como 1 (OUTPUT).

ATENÇÃO: NUNCA ALTERE A PORTA" PD0" PARA 1 (SAÍDA), EVITANDO-SE ASSIM A PERDA DE COMUNICAÇÃO SERIAL COM O COMPUTADOR.

2.2.2. A porta digital 3 pertence ao canal de portas D do microcontrolador ATmega328 e está configurado no bit 3 (PD3). Desta forma, podemos definir o registrador da porta digital 3 como saída, da seguinte maneira: DDRD = B00001000; // configura a porta digital 3 como saída e as demais portas como entrada.

2.2. Para acender e apagar o led utilizamos o registrador PORT:

2.2.1. Como já mencionamos no início do tutorial, um registrador PORT envia um sinal lógico HIGH (1) ou LOW (0) para a porta indicada.

2.2.2. A porta digital 3 faz parte do canal D do microcontrolador ATmega328 e está configurado no bit 3 (PD3).

2.2.3 Lembre-se, a ordem das portas vai de PD7 para PD0.

2.2.4. Para acender o led enviamos um sinal de alto nível (5V) diretamente para a porta digital 3, da seguinte forma: PORTD = B00001000; // envia sinal de 5 volts (HIGH) para a porta 3 e um sinal de 0V (LOW) para as demais portas.

2.2.5. Para apagar o led enviamos um sinal de baixo nível (0V) diretamente para a porta digital 3, da seguinte forma: PORTD = B00000000; // envia sinal de 0V (LOW) para todas as portas;

2.3. Observe que ao utilizarmos registradores de portas para o envio de sinais, como no exemplo acima, precisamos de apenas de 646 bytes de espaço de armazenamento para programas.

2.4. Podemos ainda reduzir mais a memória utilizada, colocando um loop infinito na função void setup():

void setup() {
  DDRD = B00001000; // define a porta 3 com saída

  while(1) {
    PORTD = B00001000; // envia sinal 5V - HIGH
    delay(500);
    PORTD = B00000000; // envia sinal 0V - LOW
    delay(500);
  } // end while
  
}

void loop() { 

}

2.4.1. Observe que no código acima utilizamos o loop infinito - while(1) - dentro do void setup(). Isto tem a mesma função o void loop(), porém, com menor espaço de armazenamento necessário.

2.4.2. Desta forma, conseguimos reduzir para 632 bytes (1%) de espaço de armazenamento para programas. Isto demonstra que reduzimos, neste pequeno programa, aproximadamente 328 bytes, ou seja, quase 35% da memória necessária do microcontrolador para executar a mesma função que o exemplo original.

3. Agora vamos avançar pouco mais. Veja abaixo algumas considerações sobre o uso de registradores definidos com números binários:

3.1. Observe que ao utilizarmos registradores nos exemplos acima, nós usamos números binários para definir se a porta é entrada ou saída (DDR) e para enviar sinais (PORT). ExS.:   DDRD = B00001000; ,  PORTD = B00001000;

3.2. Ao utilizarmos números binários, configuramos todas as 8 portas de um canal de portas. Por exemplo, o registrador DDRD = B00001000 determina que a porta 3 é saída, mas configura também as demais portas como entradas. Já o registrador PORTD = B00001000 liga o pino 3, mas desliga todos os demais pinos do canal D do microcontrolador. Isto poderá ser um problema porque não estamos ligando apenas o pino 3, mas estamos desligando os outros também.

3.3. Para resolvermos este problema e controlarmos cada pino individualmente, sem afetar os demais, vamos utilizar a lógica digital através de operadores bitwise (bit a bit). Com este recurso podemos configurar apenas o pino desejado de forma independente como mostraremos a seguir:

3.3.1. Neste tutorial não temos a intenção de explicarmos como utilizar operadores bitwise, portanto recomendo que leia o artigo Operadores Bitwise (Bit-a-Bit) do blog Diogo Matheus.

4. Usando a lógica digital para configurar registradores, obtemos:

4.1. Para imprimir o valor 1 em um bit, use sempre o padrão:

DDR |= (1 << bit que se pretende configurar como saída);

PORT |= (1<< bit que se pretende enviar HIGH);

4.1.1. No nosso exemplo, teremos as instruções: DDRD |= (1<<3); e PORTD |= (1<<3)

Obs.:  DDRD |= (1<<3); pode ser escrito da seguinte forma: DDRD = DDRD | (1<<3);

4.2. Para imprimir o valor 0 em um bit, use sempre o padrão:

DDR &= ~(1<< bit que se pretende configurar como entrada);

PORT &= ~(1 << bit que se pretende enviar LOW);

4.2.1. No nosso exemplo, teremos as instruções: DDRD &= ~(1<<3); e PORTD &= ~(1<<3);

5. Veja abaixo como fica o nosso código otimizado:

void setup() {
  DDRD |= (1<<3); // seta apenas o bit 3 como saída

  while(1) {
    PORTD |= (1<<3); // envia sinal 5V - HIGH
    delay(500);
    PORTD &= ~(1<<3); // envia sinal 0V - LOW
    delay(500);
  } // end while
  
}

void loop() { 

}

5.1. Com o código otimizado acima, nós configuramos apenas a porta do microcontrolador que desejamos utilizar. No nosso exemplo é a porta PD3 (bit 3 do canal de portas D).

5.2. No código acima, diferentemente de utilizarmos valores binários, nós configuramos cada porta independentemente sem afetarmos as portas que não estão sendo utilizadas.

5.3. Com este procedimento conseguimos reduzir ainda mias o espaço de armazenamento para programas, passando de 632 para 628 bytes.

5.4. Usando o operador bitwise XOR (^) podemos inverter o sinal 1 e 0 a cada 0,5 segundo, fazendo o led piscar da mesma foram. Leia: Operador XOR - Referência Arduino.

const byte LED = (1<<3);

void setup() {
  DDRD |= LED; // seta apenas o bit 3 como saída

  while(1) {
    PORTD ^= LED; // envia sinal 5V / inverte e envia 0V
    delay(500);
  } // end while  
}

void loop() { 

}

5.4 Assim, utilizando o operador XOR, reduzimos ainda mais a memória utilizada, agora para 590 bytes.

LEITURA DE INPUT

6. Agora que já vimos como manipular portas de saída, vamos entender agora como usar registradores com portas de entrada. Para isso vamos utilizar um botão (push puton) para ligar e desligar um led.

6.1. No exemplo vamos conectar o led no pino digital 3 e o botão no pino digital 12.

6.2. Vamos configurar o botão como INPUT_PULLUP, ou seja, utilizando o resistor interno do microcontrolador.

6.3. Este exemplo tem a mesma estrutura do Projeto 04a - Push Button como interruptor - liga e desliga, porém, de forma mais otimizada.

Veja o nosso exemplo abaixo onde utilizamos as funções digitalWrite() e digitalRead().

void setup() {
  pinMode(3, OUTPUT);
  pinMode(12, INPUT_PULLUP);
}

void loop() { 
  if (!digitalRead(12)) {
    digitalWrite(3, !digitalRead(3));
    while (!digitalRead(12)) {  }
    delay(200); 
  }
}

6.4. Quando acionamos o botão, conectado no pino 12, o valor lido pela função digitalRead() é 0, pois está configurado como PULLUP. Se invertermos o valor lido através do operador lógico NOT (!) teremos uma condição verdadeira para a expressão if(!digitalRead(12), ligando ou desligando o led. A função digitalRead() lê qual sinal o pino 3 está recebendo, invertendo-o através do operador lógico NOT (!), ou seja, se se HIGH (5V) ele inverterá para LOW (0V) e vice e versa, acendendo e apagando o led. A expressão while(!digitalRead(12)) {}; diz para o Arduino que enquanto estivemos com o botão pressionado, o botão se manterá aceso ou apagado. Ao soltarmos o botão teremos uma pausa de 200ms necessária para evitar as flutuações de sinal geradas pelo efeito "bouncing", que é a acomodação dos componentes mecânicos do botão. Referências: digitalWrite(), digitalRead() e operador NOT.

Obs.: O valor que usamos para evitar o efeito "bouncing" poderá ser alterado em função do tipo e da marca do botão usado no projeto. Recomendo valores entre 10ms e 200ms.

Dica: Você poderá testar valores diferentes até conseguir manter a estabilidade no acionamento botão. No exemplo acima, onde usamos as funções digitalWrite() e digitalRead() podemos utilizar valores menores (50ms) e ao utilizarmos registradores diretos, valores maiores (200ms), pois o sinal será enviado diretamente no pino do microcontrolador sem precisar de um gerenciamento via software, o que torna o processo bem mais rápido.

6.5. Observe que ao utilizarmos as funções digitalWrite() e digitalRead() como no exemplo acima, precisaremos de 1074 bytes (3%) de espaço de armazenamento para programas.

7. Agora vamos fazer o mesmo efeito utilizando registradores. Vamos ler o sinal da porta digital 12 (botão) e enviar o sinal HIGH ou LOW diretamente na porta digital 3 do microcontrolador.

const byte LED = (1<<3); // Conectado no pino 3 (bit 3 da Porta D)
const byte BOT = (1<<4); // Concetado no pino 12 (bit 4 da Porta B)

void setup() {
  DDRD |= LED; // Configura O pino 3 como OUTPUT (1)

  DDRB &= ~BOT; // Configura o pino 12 como INPUT (0)
  PORTB |= BOT; // Habilita o pullup interno (1) (INPUT_PULLUP)

  while (1) {
    if (!(PINB &BOT)) {
      PORTD ^= LED;
      while (!(PINB &BOT))  { }
      delay(200);
    }
  }
}

 7.1. Definimos primeiramente as constantes LED e BOT:

7.1.1. A constante LED define o bit 3 para a conexão do led no pino 3 do microcontrolador. (PORTA PD3)

7.1.2. A constante BOT define o bit 4 para a conexão do botão no pino 12 do microcontrolador. (PORTA PB4)

    

7.2. Através da expressão DDRD |= LED; definimos o pino 3 como OUTPUT, ou seja, configuramos o bit 3 da PORTA D com o valor 1.

7.3. Através da expressão DDDB &= ~BOT; definimos o pino 12 como INPUT, ou seja, configuramos o bit 4 da PORTA B com o valor 0.

7.4. Através da expressão PORTB |= BOT; habilitamos o INPUT_PULLUP interno do microcontrolador, enviando o sinal HIGH (1) para o pino 12, ou seja, para o bit 4 da PORTA PB4.

7.4.1. A configuração do INPUT_PULLUP serve para utilizarmos o resistor interno do microcontrolador e evitar flutuações indevidas de sinal. Leia Como usar push button com Arduino (programação).

7.5. Seguindo a mesma estrutura do projeto original, sendo:

7.5.1. Para fazer a leitura de uma porta de entrada, use sempre o padrão:

(PIN &(1<< bit que se pretende fazer a leitura))

7.5.1.1. No nosso exemplo, leitura da porta digital 12 (bit 4 da PORTA B), seria (PINB &(1<<4). Como definimos BOT = (1<<4), a leitura de entrada seria: (PINB &BOT).

7.5.2 Através da instrução if (!(PINB &BOT)) verificamos se o botão foi pressionado. Isto é o mesmo if (!digitalRead(12)), ou seja se o valor lido no pino 12 for 0, significa que o botão foi pressionado.

7.5.2.1.  Usando o operador bitwise XOR, a instrução PORTD ^= LED liga e desliga o led a cada vez que o botão é pressionado. Operador XOR - Referência Arduino.

7.5.2.2. Finalmente, verificamos se o botão foi liberado através da expressão while (!(PINB &BOT))  { }, ou seja, lemos a entrada no pino 12 e se o valor for 0, significa que o botão ainda está pressionado.

7.6. Desta forma, utilizando registradores, conseguimos reduzir para 600 bytes (1%) de espaço de armazenamento para programas. Isto demonstra que reduzimos, neste programa simples, aproximadamente 474 bytes, ou seja, quase 45% da memória necessária do microcontrolador para executar a mesma função que o exemplo original.

8. Agora que tivemos uma visão geral do uso de registradores de portas para leitura e escrita digital no microcontrolador ATmega328 utilizado no Arduino UNO, podemos prosseguir para o nosso projeto final, onde usamos um botão e um sensor de toque para ligar e desligar um led.

Sketch do projeto

1. Faça o dowload e abra o arquivo projeto81.ino no IDE do Arduino: DOWNLOAD - projeto81.ino

Se preferir, copie e cole o código abaixo no IDE do Arduino:

/*******************************************************************************
*
*    Projeto 81: Leitura de entrada e saída - Registradores de Portas Arduino
*    Autor: Angelo Luis Ferreira
*    Data: 06/05/2020
*                          http://squids.com.br/arduino                
*
*******************************************************************************/
const byte LED = (1<<3); //  pino digital 3 (bit 3 da Porta D)
const byte BOT = (1<<4); //  pino digital 12 (bit 4 da Porta B)
const byte TOQ = (1<<2); //  pino digital 2 (bit 2 da Porta D)

void setup() {
  DDRD |= LED; // Configura o pino 3 como OUTPUT (1)
  DDRB &= ~BOT; // Configura o pino 12 como INPUT (0)
  PORTB |= BOT; // Habilita o pullup interno (1) (INPUT_PULLUP)
  DDRD &= ~TOQ; // Configura o pino 2 como INPUT (0)

  while (1) {
    if (!(PINB &BOT) || (PIND &TOQ)) {
      PORTD ^= LED;
      while (!(PINB &BOT) || (PIND &TOQ))  { }
      delay(200);
    }
  }
}

Vídeo

Como o projeto deve funcionar

1. Ao iniciar o programa, o led estará desligado.

2. Clicando no botão ou no sensor de toque, o led se estriver desligado irá acender e se estiver ligado irá apagar.

O anúncio abaixo ajuda a manter o Squids Arduino funcionando

Comentários

×

Infomações do site / SEO








×

Adicionar Marcadores