Trilha de aprendizado · Nível 10 · Tutorial 4

Controle de GPIOs por registradores no ATmega328P

Consultar a documentação do ATmega328P e controlar um LED e uma entrada digital por registradores, preservando os demais pinos da placa própria.

  • Nível: Intermediário
  • Duração: 18 min
  • 8 passos
Controle de GPIOs por registradores no ATmega328P

O que você vai percorrer

  1. Entender o acesso ao hardware e consultar a documentação Aprenda a tratar registradores de periféricos como interfaces documentadas do hardware e a localizar as informações necessárias antes de acessá-los. 2 min
  2. Relacionar D13 e D2 às portas do ATmega328P Mapeie os conectores D13 e D2 aos bits do ATmega328P e use os símbolos da arquitetura AVR. 2 min
  3. Separar direção, comando e leitura Escolha DDRx, PORTx ou PINx de acordo com a direção, o comando e a leitura desejados. 2 min
  4. Controlar o LED sem sobrescrever a porta inteira Use máscaras em DDRB e PORTB para inicializar e comandar o LED integrado em PB5, preservando os demais bits da porta B. 2 min
  5. Ler o botão pelo nível presente no pino Configure PD2 com pull-up, leia seu nível por PIND e diferencie o comando armazenado em PORTD do sinal presente no terminal. 2 min
  6. Usar a escrita especial em PINx Use a regra especial de escrita em PINx para alternar PB5 sem afetar os demais bits da porta. 2 min
  7. Definir quem controla cada pino Defina uma responsabilidade única para os GPIOs da prática e reconheça o limite das máscaras diante de outros escritores. 1 min
  8. Aplicar e validar o controle por registradores Aplique em sua própria placa um programa completo que lê D2 e controla o LED de D13 por registradores AVR. 4 min

O que você vai aprender

  • Relacionar D13 e D2 aos respectivos bits das portas do ATmega328P.
  • Configurar direção, comando e leitura com os registradores apropriados.
  • Alterar um bit de configuração sem sobrescrever intencionalmente os demais.
  • Distinguir uma escrita comum em PORTx da função especial de escrita em PINx.

Antes de começar

  • Primeiro programa no Arduino Uno R3
  • Controle de LEDs com saídas digitais
  • Leitura de botões nas entradas do Uno R3
  • Máscaras e operações de bits em C/C++

Passo 1 de 8

Entender o acesso ao hardware e consultar a documentação

Aprenda a tratar registradores de periféricos como interfaces documentadas do hardware e a localizar as informações necessárias antes de acessá-los.

Por que consultar a documentação?

Registrador não é variável comum

Um registrador de periférico é uma posição especial ligada ao hardware do microcontrolador. Ao acessá-la, o programa não está apenas manipulando um valor na memória: uma leitura pode consultar um estado do circuito, e uma escrita pode configurar ou acionar uma função do periférico.

Isso difere de uma variável comum em C/C++, cujo valor existe como dado do programa. Também difere dos registradores de armazenamento vistos em lógica digital: aqui, os bits têm significado definido pela documentação do periférico.

Por isso, antes de escrever em um registrador, confirme o efeito documentado daquele acesso.

Dado do programa e acesso ao hardware

Diagrama comparando uma variável armazenada na memória com um registrador de periférico conectado aos circuitos e pinos de GPIO.

Em um registrador de periférico, o significado de cada acesso depende da função documentada para aqueles bits.

Como ler um quadro de registrador

Roteiro de consulta

Ao trabalhar com GPIO no Uno R3, consulte a folha de dados do ATmega328P — o microcontrolador da placa de referência. Siga este roteiro:

  1. Confirme que o componente é o ATmega328P.
  2. Localize a seção sobre portas de entrada e saída digital (I/O Ports).
  3. Encontre o registrador de interesse na descrição da porta.
  4. Leia o quadro do registrador e sua explicação detalhada.

Não use somente o nome parecido de um registrador como regra de programação. A descrição informa o que seus bits representam e quais consequências uma leitura ou escrita pode ter.

O que procurar no quadro

Um quadro de registrador costuma trazer informações como estas. Elas respondem perguntas diferentes e devem ser lidas em conjunto.

Exemplo do tipo de informação encontrado na documentação; os nomes são genéricos nesta etapa.
InformaçãoPergunta respondida
Nome do registradorQual símbolo o código acessa?
Campos e posições dos bitsQual bit ou grupo de bits representa a função?
Valor após resetEm qual condição os bits começam após a reinicialização?
Acesso R/W, R ou WA leitura e/ou escrita é permitida?
Descrição do bit e notasO que ler ou escrever realmente faz?

Prática de leitura documental

Dica

R/W não descreve todo o comportamento

A marcação R/W informa que o campo pode ser lido e escrito, mas ela não basta para prever o resultado. É necessário ler a descrição do bit e as notas da seção.

Por exemplo, a leitura pode devolver um estado físico; a escrita pode alterar uma configuração, solicitar uma ação ou ter regras específicas para os valores 0 e 1. Portanto, trate o quadro e o texto explicativo como uma única fonte de informação.

Nos próximos passos, usaremos quadros autocontidos com as informações necessárias do ATmega328P, sempre verificando nome, bit, reset, acesso e efeito.

Cheque sua consulta

Associe cada pergunta à informação documental mais adequada.

Faça as associações.

Toque em um item e depois no par correspondente.

Passo 2 de 8

Relacionar D13 e D2 às portas do ATmega328P

Mapeie os conectores D13 e D2 aos bits do ATmega328P e use os símbolos da arquitetura AVR.

Identifique os dois pinos da prática

Da placa ao microcontrolador

No Uno R3 usado nesta prática, o nome do conector na placa não é o mesmo nome usado dentro do ATmega328P. A correspondência que vamos usar é:

  • D13 → PB5
  • D2 → PD2

Em PB5, a letra B identifica a porta e o número 5 identifica a posição do bit nessa porta. Analogamente, PD2 é o bit 2 da porta D. Esses nomes permitem selecionar o bit correto nos registradores da porta nas próximas etapas.

Conectores e bits selecionados

Diagrama relacionando dois conectores digitais do Uno R3 aos bits 5 da porta B e 2 da porta D.

A prática usa D13 como PB5 e D2 como PD2.

Não confunda conector com bit

Nomes em camadas

Quatro identificações podem aparecer ao consultar esquemas e documentação. Elas respondem a perguntas diferentes; não são sinônimos.

Compare as identificações

Referência para o Uno R3 com ATmega328P. O terminal físico depende do encapsulamento e serve para conferir o hardware, não para substituir o nome PB5 ou PD2 no código.

Use o número Arduino para localizar o conector; use porta e bit para acessar GPIO por registradores.
IdentificaçãoExemploO que informa
Número ArduinoD13 ou D2Conector na placa e número usado pela API Arduino
Porta e bitPB5 ou PD2Porta do microcontrolador e posição no byte
Posição do bit5 ou 2Bit selecionado na respectiva porta
Terminal físicoDepende do encapsulamentoPino físico do ATmega328P no chip

Acesse os símbolos do ATmega328P

Símbolos específicos da arquitetura

Em uma compilação para AVR, o cabeçalho avr/io.h disponibiliza os símbolos da arquitetura selecionada, incluindo registradores como PORTB e nomes de bits como PB5 e PD2.

Ele não converte automaticamente números Arduino: 13 não significa “bit 13”. Para esta placa, D13 é PB5; D2 é PD2. Em um Uno R4 ou em outra placa, o microcontrolador e o mapeamento podem ser diferentes. Consulte sempre a documentação e a definição da placa usada.

Inclua o cabeçalho AVR

Complete a diretiva que torna disponíveis os símbolos AVR usados neste tutorial.

Cabeçalho de E/S AVR

Preencha somente o nome do cabeçalho entre os sinais < e >.

cpp
#include <>

void setup() {
}

void loop() {
}

Pratique a associação

Confira o mapeamento

Associe cada identificação à sua correspondência no ATmega328P usado pelo Uno R3.

Toque em um item e depois no par correspondente.

Passo 3 de 8

Separar direção, comando e leitura

Escolha DDRx, PORTx ou PINx de acordo com a direção, o comando e a leitura desejados.

Escolha o registrador pela operação

Três papéis para cada porta

Em cada porta do ATmega328P, três famílias de registradores respondem a perguntas diferentes:

  • DDRx: o pino será entrada ou saída?
  • PORTx: qual comando aplicar conforme a direção escolhida?
  • PINx: qual nível digital está presente no pino?

O x é a letra da porta. Assim, PB5 usa DDRB, PORTB e PINB; PD2 usa DDRD, PORTD e PIND.

Para um bit de DDRx, 0 configura entrada e 1 configura saída. Depois dessa escolha, o mesmo bit de PORTx ganha um significado dependente da direção.

Como os registradores se relacionam ao pino

Diagrama de uma célula GPIO com caminhos separados para direção, comando de saída ou pull-up e leitura do nível do pino.

DDRx seleciona a direção; PORTx atua sobre a saída ou o pull-up; PINx é usado para consultar o nível digital.

Interprete os dois bits juntos

Combinações de direção e comando

A tabela vale quando o pino está na função GPIO e nas condições padrão desta prática, com os pull-ups globais habilitados. Ela mostra o efeito dos bits correspondentes de DDRx e PORTx para um mesmo pino.

Interpretação de um bit de DDRx junto ao bit correspondente de PORTx.
Bit em DDRxBit em PORTxModo do pinoEfeito
00EntradaPull-up interno desabilitado
01EntradaPull-up interno habilitado
10SaídaComanda nível baixo
11SaídaComanda nível alto

Reset não é o início do setup

Use PINx quando a tarefa for consultar o nível digital presente no pino. Por exemplo, para a porta D, PIND reúne os níveis dos bits PD0 a PD7.

Após um reset, os registradores DDRx e PORTx começam com bits em 0: os pinos começam como entradas e sem pull-up habilitado por esses bits. Isso descreve o estado de reset documentado, não uma promessa sobre o instante em que seu setup() começa: antes dele, o bootloader e a inicialização do ambiente podem ter executado.

Pratique a seleção correta

Configure a entrada

Para configurar D2 (PD2) como entrada com pull-up, qual combinação é adequada?

Escolha direção e comando

Você quer colocar PB5 como saída em nível alto. Qual operação corresponde a essa intenção?

Passo 4 de 8

Controlar o LED sem sobrescrever a porta inteira

Use máscaras em DDRB e PORTB para inicializar e comandar o LED integrado em PB5, preservando os demais bits da porta B.

Selecionar PB5 sem apagar os vizinhos

Uma porta reúne vários pinos

D13 corresponde a PB5. Logo, o bit 5 é o ponto de interesse em DDRB e PORTB.

Uma atribuição como DDRB = 0b00100000; grava os oito bits do registrador: além de colocar PB5 em 1, força todos os outros bits para 0. Isso pode mudar a configuração de pinos que esta montagem não usa.

Use uma máscara de um bit para alterar somente PB5:

  • |= ativa o bit selecionado e preserva os demais;
  • &= ~ limpa o bit selecionado e preserva os demais.

Essa preservação vale para a operação realizada: os outros bits mantêm o valor que tinham imediatamente antes dela.

Um bit selecionado, sete preservados

Comparação entre uma alteração seletiva de um único bit da porta B e uma escrita que substitui todos os bits da porta.

A máscara concentra a mudança em PB5; uma atribuição do byte inteiro também alcança os bits vizinhos.

Dica

Máscara do LED

Com avr/io.h, PB5 representa a posição 5. A máscara (1U << PB5) seleciona esse bit, correspondente a D13 no Uno R3.

Os bits não usados na montagem continuam pertencendo a DDRB e PORTB. Preservá-los significa que esta instrução não pretende substituí-los por outro valor.

Inicializar e comandar o LED

Defina o comando antes da direção

Partindo da condição de entrada usada nesta prática, defina primeiro o comando baixo em PORTB e depois habilite PB5 como saída em DDRB. Assim, quando o pino se tornar saída, já terá comando baixo.

O sketch abaixo é compilável: o cabeçalho fica no início do arquivo e as instruções executáveis ficam dentro de setup(). Ele não afirma qual era o estado da porta antes de setup() começar.

PB5 inicialmente baixo e depois como saída

Exemplo completo de inicialização para o sketch.

cpp
#include <avr/io.h>

const uint8_t LED_MASK = (uint8_t)(1U << PB5);

void setup() {
  // Nesta prática, parte-se de PB5 como entrada.
  DDRB  &= (uint8_t)~LED_MASK;

  // Comando baixo e pull-up de PB5 desabilitado.
  PORTB &= (uint8_t)~LED_MASK;

  // PB5 passa a saída já com comando baixo.
  DDRB  |= LED_MASK;
}

void loop() {
}

Comande o LED seletivamente

Depois da inicialização, PB5 é saída; portanto, seu bit em PORTB comanda o LED integrado:

  • PORTB |= LED_MASK; comanda nível alto e acende o LED;
  • PORTB &= (uint8_t)~LED_MASK; comanda nível baixo e apaga o LED.

As duas instruções selecionam somente PB5. Os outros bits de PORTB não são intencionalmente substituídos.

Acender e apagar D13

Use estas instruções dentro de uma função, após a inicialização de PB5.

cpp
// Acende o LED integrado: PB5 em nível alto.
PORTB |= LED_MASK;

// Apaga o LED integrado: PB5 em nível baixo.
PORTB &= (uint8_t)~LED_MASK;

Aplicar as máscaras

Prática: escolha os operadores

Complete as duas operações seletivas. A primeira deve configurar PB5 como saída; a segunda deve deixar o LED apagado. Em ambas, os bits não selecionados devem ser preservados.

Complete o acesso seletivo

Preencha somente os operadores de atribuição composta.

cpp
DDRB   LED_MASK;              // PB5 como saída
PORTB  (uint8_t)~LED_MASK;   // LED apagado

Conferir a sequência

Prática: ordem da inicialização

Agora organize a inicialização completa a partir da condição de entrada adotada neste exemplo. A direção só deve ser habilitada depois que o comando baixo estiver definido em PORTB.

Ordene as operações

Coloque as etapas na ordem recomendada.

  1. PORTB &= (uint8_t)~LED_MASK; // comando baixo
  2. DDRB |= LED_MASK; // PB5 como saída
  3. DDRB &= (uint8_t)~LED_MASK; // PB5 como entrada

Passo 5 de 8

Ler o botão pelo nível presente no pino

Configure PD2 com pull-up, leia seu nível por PIND e diferencie o comando armazenado em PORTD do sinal presente no terminal.

1. Prepare D2 como entrada com pull-up

Configure somente PD2

No Uno R3 com ATmega328P, D2 corresponde a PD2, o bit 2 da porta D. Para usar o botão ligado entre D2 e GND, PD2 deve permanecer como entrada e ter seu pull-up interno habilitado.

Use máscaras para modificar somente esse bit:

  • Em DDRD, limpe PD2 para selecionar entrada.
  • Em PORTD, ative PD2 para habilitar o pull-up dessa entrada.

As operações preservam os demais bits de DDRD e PORTD: elas não pretendem substituir a configuração dos outros pinos da porta.

Ligação com pull-up interno

Esquema de D2 ligado por um botão a GND, com o pull-up interno conectado ao pino PD2.

Com o botão solto, o pull-up mantém D2 em nível alto. Ao pressionar, o botão liga D2 ao GND.

Configuração seletiva em setup

Este sketch completo mostra o escopo correto das instruções: a constante fica fora das funções, mas a configuração dos registradores ocorre dentro de setup().

cpp
#include <avr/io.h>

const uint8_t BUTTON_MASK = (uint8_t)(1U << PD2);

void setup() {
  // PD2 como entrada.
  DDRD &= (uint8_t)~BUTTON_MASK;

  // Pull-up interno de PD2 habilitado.
  PORTD |= BUTTON_MASK;
}

void loop() {
}

2. Leia o nível que chega ao pino

Comando armazenado não é nível lido

PORTD e PIND respondem a perguntas diferentes:

  • Ler PORTD consulta o comando armazenado. Quando PD2 é entrada, o bit em 1 mantém seu pull-up habilitado.
  • Ler PIND consulta o nível digital presente no terminal.

Portanto, nesta prática o bit 2 de PORTD continua em 1 tanto com o botão solto quanto pressionado. Ao pressionar, o botão conecta D2 ao GND; então o bit 2 lido em PIND passa a 0.

O que cada leitura mostra

Considere PD2 como entrada com pull-up habilitado.

O botão muda o nível em PIND; ele não desabilita o pull-up cujo comando está armazenado em PORTD.
Estado do botãoBit 2 em PORTDBit 2 em PINDInterpretação
Solto11Pull-up mantém o pino alto
Pressionado10Botão conecta o pino ao GND

Teste de botão pressionado

A máscara seleciona apenas PD2. Como a ligação é ativa em nível baixo, resultado mascarado igual a zero significa botão pressionado.

cpp
bool pressionado = (PIND & BUTTON_MASK) == 0U;

Atenção

Limite e cuidado na leitura

PINx informa somente se o hardware reconhece nível digital baixo ou alto. Ele não mede tensão, corrente nem comprova sozinho a integridade elétrica de uma saída.

Nesta ligação, conecte o botão somente entre D2 e GND. Não aplique tensão externa a D2 e não tente criar divergências forçando um pino configurado como saída.

3. Complete o acesso ao botão

Pratique configuração e leitura

Complete os operadores e o registrador. O trecho é um sketch compilável: as lacunas configuram PD2 e consultam o botão ativo em nível baixo.

cpp
#include <avr/io.h>

const uint8_t BUTTON_MASK = (uint8_t)(1U << PD2);

void setup() {
  DDRD   (uint8_t)~BUTTON_MASK;
  PORTD  BUTTON_MASK;
}

void loop() {
  bool pressionado = ( & BUTTON_MASK) == 0U;
}

4. Diferencie comando e leitura

Preveja os bits

Com PD2 como entrada e pull-up habilitado, qual combinação é esperada enquanto o botão está pressionado?

Passo 6 de 8

Usar a escrita especial em PINx

Use a regra especial de escrita em PINx para alternar PB5 sem afetar os demais bits da porta.

Entenda o efeito da escrita

Uma escrita com efeito especial

No ATmega328P, PINx é o registrador usado para ler o nível presente nos pinos. Porém, a documentação define um efeito especial para a escrita: escrever 1 em uma posição de PINx alterna o bit correspondente em PORTx; escrever 0 não altera esse bit.

Assim, PINB não recebe um novo “valor de entrada”. A escrita aciona uma operação sobre o comando armazenado em PORTB.

Para PB5, ligado ao D13 no Uno R3, a máscara é (1U << PB5).

O bit 1 alterna; o bit 0 preserva

Diagrama mostrando que escrever 1 no bit 5 de PINB alterna o bit 5 de PORTB, enquanto escrever 0 não produz alteração.

O efeito é sobre o bit correspondente de PORTB, não sobre o nível que PINB retorna em uma leitura.

Regra por bit

Considere somente um bit da porta:

  • Se PORTB5 está em 0 e você escreve 1 em PINB5, PORTB5 passa a 1.
  • Se PORTB5 está em 1 e você escreve 1 em PINB5, PORTB5 passa a 0.
  • Se você escreve 0 em PINB5, PORTB5 permanece como está.

Se PB5 for saída, alternar PORTB5 alterna o comando do LED. Se o pino for entrada, a mesma operação alterna a habilitação do pull-up desse pino. Portanto, essa regra não é exclusiva de saídas.

Efeito de uma escrita no bit correspondente de PINx.
Bit escrito em PINxEfeito no bit de PORTx
0Não altera
1Alterna 0 para 1 ou 1 para 0

Escreva somente a máscara

Alternância seletiva de PB5

Para alternar somente PB5, escreva diretamente a máscara desejada em PINB:

PINB = (1U << PB5);

Essa atribuição coloca 1 apenas no bit 5 da escrita. Os demais bits recebem 0 e, pela regra especial, não são alterados. Não é necessário ler PINB antes.

Escrita direta da máscara

Depois de configurar PB5 como saída, uma função curta pode alternar o LED integrado assim:

cpp
#include <avr/io.h>

void alternarLed() {
  PINB = (1U << PB5);
}

Exemplo

PORTB e PINB não são intercambiáveis

Compare com uma escrita comum em PORTB:

  • PORTB |= (1U << PB5); coloca o comando de PB5 em 1.
  • PORTB &= ~(1U << PB5); coloca o comando de PB5 em 0.
  • PINB = (1U << PB5); pede ao hardware que inverta o comando atual de PB5.

A primeira família altera um valor armazenado em PORTB por operações usuais. A última aciona o efeito especial documentado para uma escrita em PINB.

Evite alternâncias não desejadas

Atenção

Não misture leitura e escrita em PINB

Evite tratar PINB como um registrador comum para operações de leitura-modificação-escrita.

Por exemplo, não use PINB |= (1U << PB5); para alternar PB5. Antes de escrever, essa expressão lê PINB, que representa os níveis presentes nos pinos. Qualquer outro bit lido como 1 pode voltar na escrita e alternar o bit correspondente de PORTB sem que você pretendesse isso.

Para a operação especial, use atribuição direta da máscara: PINB = (1U << PB5);.

Escolha a escrita segura

Qual instrução alterna PB5 sem incorporar os demais níveis lidos em PINB?

Escreva pelo menos 10 caracteres (0/10).

Pratique a seleção da escrita

Aplique a regra especial

Complete a instrução que pede ao ATmega328P para alternar somente o comando associado a PB5.

Escreva pelo menos 12 caracteres (0/12).

Verificação rápida

Qual linha é a forma correta de alternar somente PB5 pela escrita especial em PINB?

Passo 7 de 8

Definir quem controla cada pino

Defina uma responsabilidade única para os GPIOs da prática e reconheça o limite das máscaras diante de outros escritores.

Reconheça o recurso compartilhado

Um pino não tem dois donos

Um pino, um único hardware

pinMode(), digitalWrite() e digitalRead() são funções da API Arduino. Já DDRx, PORTx e PINx são acessos diretos aos registradores do ATmega328P. Eles não controlam hardwares separados: ambos chegam aos mesmos circuitos dos pinos.

Assim, uma chamada da API pode alterar uma configuração feita por registradores — e uma escrita em registrador pode mudar o estado que uma chamada posterior da API encontrará.

Nesta prática, defina uma responsabilidade simples: o código de registradores controla D13/PB5 e D2/PD2. Não use chamadas da API Arduino para configurar ou comandar esses dois pinos no mesmo programa.

Os dois caminhos chegam ao mesmo GPIO

Diagrama mostrando API Arduino e registradores chegando ao mesmo circuito de GPIO do microcontrolador.

A API e o acesso direto são duas formas de escrever ou consultar o mesmo hardware.

Preserve bits e evite outros escritores

Máscara não é exclusividade

As máscaras continuam importantes: uma operação como DDRB |= _BV(DDB5); pretende mudar apenas PB5 e preservar os demais bits de DDRB.

Mas isso não impede outro trecho de código de escrever depois em DDRB, PORTB, DDRD ou PORTD. A máscara protege os bits não selecionados naquela operação; ela não reserva a porta nem coordena autores diferentes.

Por isso, durante a prática, bibliotecas e outros trechos não devem alterar as portas B e D usadas por D13 e D2.

Preservar não é impedir conflito

Compare o papel de cada medida nesta prática.

As duas medidas são necessárias, mas resolvem problemas diferentes.
MedidaO que ela fazO que ela não garante
Máscara em `DDRB` ou `PORTB`Altera somente o bit escolhidoQue outro código não escreva na porta
Responsabilidade definidaEvita comandos concorrentes em D13 e D2Que uma máscara deixe de ser necessária
Sem reconfigurar temporizadoresMantém o escopo da prática em GPIOControle de temporização; isso será estudado depois

Estabeleça um responsável

Exemplo

Uma chamada pode desfazer a outra

Este trecho é inadequado para a prática, pois mistura dois responsáveis por D13:

void setup() {
  PORTB &= (uint8_t)~_BV(PORTB5);
  DDRB  |= _BV(DDB5);

  pinMode(13, INPUT); // desfaz a direção de saída de PB5
}

pinMode(13, INPUT) atua sobre o mesmo PB5 e volta o bit de direção a entrada. O LED pode deixar de responder ao comando esperado.

Use setup() e loop() normalmente: eles pertencem ao ambiente Arduino. O que muda é que D13 e D2 serão configurados, comandados e lidos somente pelos registradores neste programa.

Dica

Contrato da prática

Para o programa integrado do próximo passo, adote este limite:

  • D13/PB5: DDRB, PORTB e, quando desejado, escrita direta em PINB;
  • D2/PD2: DDRD, PORTD e PIND;
  • nenhuma chamada pinMode, digitalWrite ou digitalRead para D13 e D2;
  • nenhuma biblioteca ou outro trecho alterando essas portas durante a execução;
  • nenhum temporizador é reconfigurado nesta prática.

O núcleo Arduino continua fornecendo a estrutura setup()/loop() e a gravação do sketch.

Cheque seu entendimento

Verifique a responsabilidade

Considere esta sequência em setup():

DDRB |= _BV(DDB5);
pinMode(13, INPUT);

É correto afirmar que a máscara na primeira linha garante que PB5 continuará sendo saída, mesmo após a segunda linha?

Passo 8 de 8

Aplicar e validar o controle por registradores

Aplique em sua própria placa um programa completo que lê D2 e controla o LED de D13 por registradores AVR.

Prepare a prática

Objetivo da prática

Nesta aplicação, o LED integrado de D13 acompanha o nível mantido do botão em D2: solto, LED apagado; pressionado, LED aceso. Não há contagem de cliques, temporização nem tratamento de rebote.

Use um Arduino Uno R3 com ATmega328P, cabo USB de dados, um botão e fios. O programa acessa PB5 (D13) e PD2 (D2) diretamente pelos registradores AVR.

Ligação e papéis dos pinos

Diagrama de uma placa compatível com Uno R3: botão entre D2 e GND e LED integrado associado a D13; D2 corresponde a PD2 e D13 a PB5.

O botão fecha a entrada D2 para GND. O pull-up interno mantém a leitura alta quando o botão está solto.

Atenção

Antes de montar

Sequência segura

  1. Deixe o botão desconectado e grave o programa.
  2. Desconecte o cabo USB, retirando a alimentação da placa.
  3. Monte somente a ligação entre D2, botão e GND.
  4. Confira se o botão não conecta D2 a 5 V nem a outra fonte.
  5. Alimente novamente apenas pelo USB.

Não aplique tensão externa a D2 e não use outra fonte enquanto o USB estiver conectado.

Confira a lógica antes do programa completo

Complete os acessos essenciais

Complete as lacunas que levam PB5 explicitamente à condição de saída inicialmente baixa, configuram PD2 como entrada com pull-up e leem o nível presente em D2. As máscaras já selecionam somente os bits da prática.

A primeira operação de DDRB não presume qual direção PB5 tinha antes de setup(): ela o coloca como entrada antes de definir o comando baixo.

Configuração seletiva

Preencha operadores e o registrador de leitura.

cpp
const uint8_t LED_MASK = (uint8_t)(1U << PB5);
const uint8_t BUTTON_MASK = (uint8_t)(1U << PD2);

void setup() {
  DDRB   (uint8_t)~LED_MASK;
  PORTB  (uint8_t)~LED_MASK;
  DDRB   LED_MASK;

  DDRD   (uint8_t)~BUTTON_MASK;
  PORTD  BUTTON_MASK;
}

bool botaoPressionado() {
  return ( & BUTTON_MASK) == 0U;
}

Grave e observe localmente

Programa completo

Crie um sketch novo, substitua seu conteúdo por este programa, compile e grave no Uno R3 com o botão ainda desconectado.

cpp
#include <avr/io.h>

const uint8_t LED_MASK = (uint8_t)(1U << PB5);
const uint8_t BUTTON_MASK = (uint8_t)(1U << PD2);

void setup() {
  // Não presume a direção anterior de PB5.
  DDRB  &= (uint8_t)~LED_MASK;

  // Define comando baixo enquanto PB5 é entrada.
  PORTB &= (uint8_t)~LED_MASK;

  // Só então PB5 passa a saída.
  DDRB  |= LED_MASK;

  // PD2 como entrada com pull-up interno.
  DDRD  &= (uint8_t)~BUTTON_MASK;
  PORTD |= BUTTON_MASK;
}

void loop() {
  bool pressionado = (PIND & BUTTON_MASK) == 0U;

  if (pressionado) {
    PORTB |= LED_MASK;
  } else {
    PORTB &= (uint8_t)~LED_MASK;
  }
}
Saída esperada
Botão solto: LED integrado de D13 apagado.
Botão mantido pressionado: LED integrado de D13 aceso.

O sketch não envia texto ao computador. A saída esperada é observável no LED integrado depois da montagem do botão e da nova alimentação pelo USB.

Monte e valide

Depois de gravar, desligue a placa pelo cabo USB, faça a ligação D2–botão–GND mostrada anteriormente e reconecte o USB.

Observe o LED integrado:

  • botão solto: apagado;
  • botão mantido pressionado: aceso.

Se o resultado divergir, desligue pelo USB e confira a posição dos contatos do botão e a ligação ao GND. Não force níveis externos em D2.

Revise e conclua

Registre a validação

Relate sua observação após testar. Inclua o estado do LED com o botão solto e enquanto ele permaneceu pressionado. Em seguida, explique por que |= e &= ~ não escrevem intencionalmente um novo valor nos outros bits da porta.

Escreva pelo menos 80 caracteres (0/80).

Síntese final

Escolha o registrador pelo efeito desejado, não apenas por seu nome.

A escrita especial em PINB é diferente de uma escrita comum em PORTB.
OperaçãoRegistrador ou escritaEfeito nesta prática
Definir PB5 como saídaDDRBBit PB5 recebe 1
Apagar ou acender LEDPORTBComando baixo ou alto em PB5
Habilitar pull-up de D2PORTDBit PD2 recebe 1
Ler o botãoPINDNível digital presente em PD2
Alternar PB5 uma vezPINB = LED_MASKEscrever 1 alterna PORTB5

Competência adquirida

Você aplicou o acesso direto aos GPIOs do ATmega328P: configurou D13/PB5 e D2/PD2, leu o nível por PIND e comandou o LED por PORTB, preservando os demais bits nas operações seletivas.

Lembre a distinção final: PORTB |= LED_MASK comanda PB5 em nível alto; PORTB &= ~LED_MASK comanda nível baixo; PINB = LED_MASK aciona a alternância especial de PB5. Evite PINB |= LED_MASK, pois uma leitura prévia pode provocar alternâncias indesejadas em outros bits.

Conclusão

Parabéns! Você concluiu: Controle de GPIOs por registradores no ATmega328P

Muito bem! Você está pronto para avançar para intervalos com o Timer1 em modo CTC.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso