
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.
Trilha de aprendizado · Nível 10 · Tutorial 4
Consultar a documentação do ATmega328P e controlar um LED e uma entrada digital por registradores, preservando os demais pinos da placa própria.
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
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
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
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
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
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
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
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

Passo 1 de 8
Aprenda a tratar registradores de periféricos como interfaces documentadas do hardware e a localizar as informações necessárias antes de acessá-los.
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.

Em um registrador de periférico, o significado de cada acesso depende da função documentada para aqueles bits.
Ao trabalhar com GPIO no Uno R3, consulte a folha de dados do ATmega328P — o microcontrolador da placa de referência. Siga este roteiro:
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.
Um quadro de registrador costuma trazer informações como estas. Elas respondem perguntas diferentes e devem ser lidas em conjunto.
| Informação | Pergunta respondida |
|---|---|
| Nome do registrador | Qual símbolo o código acessa? |
| Campos e posições dos bits | Qual bit ou grupo de bits representa a função? |
| Valor após reset | Em qual condição os bits começam após a reinicialização? |
| Acesso R/W, R ou W | A leitura e/ou escrita é permitida? |
| Descrição do bit e notas | O que ler ou escrever realmente faz? |
Dica
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.
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
Mapeie os conectores D13 e D2 aos bits do ATmega328P e use os símbolos da arquitetura AVR.
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 é:
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.

A prática usa D13 como PB5 e D2 como PD2.
Quatro identificações podem aparecer ao consultar esquemas e documentação. Elas respondem a perguntas diferentes; não são sinônimos.
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.
| Identificação | Exemplo | O que informa |
|---|---|---|
| Número Arduino | D13 ou D2 | Conector na placa e número usado pela API Arduino |
| Porta e bit | PB5 ou PD2 | Porta do microcontrolador e posição no byte |
| Posição do bit | 5 ou 2 | Bit selecionado na respectiva porta |
| Terminal físico | Depende do encapsulamento | Pino físico do ATmega328P no chip |
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.
Complete a diretiva que torna disponíveis os símbolos AVR usados neste tutorial.
Preencha somente o nome do cabeçalho entre os sinais < e >.
#include <>
void setup() {
}
void loop() {
}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
Escolha DDRx, PORTx ou PINx de acordo com a direção, o comando e a leitura desejados.
Em cada porta do ATmega328P, três famílias de registradores respondem a perguntas diferentes:
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.

DDRx seleciona a direção; PORTx atua sobre a saída ou o pull-up; PINx é usado para consultar o nível digital.
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.
| Bit em DDRx | Bit em PORTx | Modo do pino | Efeito |
|---|---|---|---|
| 0 | 0 | Entrada | Pull-up interno desabilitado |
| 0 | 1 | Entrada | Pull-up interno habilitado |
| 1 | 0 | Saída | Comanda nível baixo |
| 1 | 1 | Saída | Comanda nível alto |
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.
Para configurar D2 (PD2) como entrada com pull-up, qual combinação é adequada?
Você quer colocar PB5 como saída em nível alto. Qual operação corresponde a essa intenção?

Passo 4 de 8
Use máscaras em DDRB e PORTB para inicializar e comandar o LED integrado em PB5, preservando os demais bits da porta B.
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.

A máscara concentra a mudança em PB5; uma atribuição do byte inteiro também alcança os bits vizinhos.
Dica
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.
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.
Exemplo completo de inicialização para o sketch.
#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() {
}
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.
Use estas instruções dentro de uma função, após a inicialização de PB5.
// 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;
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.
Preencha somente os operadores de atribuição composta.
DDRB LED_MASK; // PB5 como saída
PORTB (uint8_t)~LED_MASK; // LED apagadoAgora 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.
Coloque as etapas na ordem recomendada.

Passo 5 de 8
Configure PD2 com pull-up, leia seu nível por PIND e diferencie o comando armazenado em PORTD do sinal presente no terminal.
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:
DDRD, limpe PD2 para selecionar entrada.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.

Com o botão solto, o pull-up mantém D2 em nível alto. Ao pressionar, o botão liga D2 ao GND.
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().
#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() {
}
PORTD e PIND respondem a perguntas diferentes:
PORTD consulta o comando armazenado. Quando PD2 é entrada, o bit em 1 mantém seu pull-up habilitado.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.
Considere PD2 como entrada com pull-up habilitado.
| Estado do botão | Bit 2 em PORTD | Bit 2 em PIND | Interpretação |
|---|---|---|---|
| Solto | 1 | 1 | Pull-up mantém o pino alto |
| Pressionado | 1 | 0 | Botão conecta o pino ao GND |
A máscara seleciona apenas PD2. Como a ligação é ativa em nível baixo, resultado mascarado igual a zero significa botão pressionado.
bool pressionado = (PIND & BUTTON_MASK) == 0U;Atenção
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.
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.
#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;
}
Com PD2 como entrada e pull-up habilitado, qual combinação é esperada enquanto o botão está pressionado?

Passo 6 de 8
Use a regra especial de escrita em PINx para alternar PB5 sem afetar os demais bits da porta.
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 efeito é sobre o bit correspondente de PORTB, não sobre o nível que PINB retorna em uma leitura.
Considere somente um bit da porta:
PORTB5 está em 0 e você escreve 1 em PINB5, PORTB5 passa a 1.PORTB5 está em 1 e você escreve 1 em PINB5, PORTB5 passa a 0.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.
| Bit escrito em PINx | Efeito no bit de PORTx |
|---|---|
| 0 | Não altera |
| 1 | Alterna 0 para 1 ou 1 para 0 |
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.
Depois de configurar PB5 como saída, uma função curta pode alternar o LED integrado assim:
#include <avr/io.h>
void alternarLed() {
PINB = (1U << PB5);
}Exemplo
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.
Atenção
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);.
Qual instrução alterna PB5 sem incorporar os demais níveis lidos em PINB?
Escreva pelo menos 10 caracteres (0/10).
Complete a instrução que pede ao ATmega328P para alternar somente o comando associado a PB5.
Escreva pelo menos 12 caracteres (0/12).
Qual linha é a forma correta de alternar somente PB5 pela escrita especial em PINB?

Passo 7 de 8
Defina uma responsabilidade única para os GPIOs da prática e reconheça o limite das máscaras diante de outros escritores.
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.

A API e o acesso direto são duas formas de escrever ou consultar o mesmo hardware.
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.
Compare o papel de cada medida nesta prática.
| Medida | O que ela faz | O que ela não garante |
|---|---|---|
| Máscara em `DDRB` ou `PORTB` | Altera somente o bit escolhido | Que outro código não escreva na porta |
| Responsabilidade definida | Evita comandos concorrentes em D13 e D2 | Que uma máscara deixe de ser necessária |
| Sem reconfigurar temporizadores | Mantém o escopo da prática em GPIO | Controle de temporização; isso será estudado depois |
Exemplo
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
Para o programa integrado do próximo passo, adote este limite:
DDRB, PORTB e, quando desejado, escrita direta em PINB;DDRD, PORTD e PIND;pinMode, digitalWrite ou digitalRead para D13 e D2;O núcleo Arduino continua fornecendo a estrutura setup()/loop() e a gravação do sketch.
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
Aplique em sua própria placa um programa completo que lê D2 e controla o LED de D13 por registradores AVR.
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.

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
Sequência segura
Não aplique tensão externa a D2 e não use outra fonte enquanto o USB estiver conectado.
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.
Preencha operadores e o registrador de leitura.
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;
}Crie um sketch novo, substitua seu conteúdo por este programa, compile e grave no Uno R3 com o botão ainda desconectado.
#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;
}
}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.
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:
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.
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).
Escolha o registrador pelo efeito desejado, não apenas por seu nome.
| Operação | Registrador ou escrita | Efeito nesta prática |
|---|---|---|
| Definir PB5 como saída | DDRB | Bit PB5 recebe 1 |
| Apagar ou acender LED | PORTB | Comando baixo ou alto em PB5 |
| Habilitar pull-up de D2 | PORTD | Bit PD2 recebe 1 |
| Ler o botão | PIND | Nível digital presente em PD2 |
| Alternar PB5 uma vez | PINB = LED_MASK | Escrever 1 alterna PORTB5 |
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.
Parabéns! Você concluiu: Controle de GPIOs por registradores no ATmega328P
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks