Aula 02 - Entradas e Saídas Digitais, Debounce de Botões e Modulação PWM


1. Modos de Operação dos Pinos Digitais (GPIO)

O microcontrolador ATmega328P na placa Arduino Uno R3 disponibiliza 14 pinos digitais de uso geral numerados de D0 a D13 (além de permitir que os 6 pinos analógicos A0 a A5 sejam reconfigurados como pinos digitais D14 a D19).

A função nativa pinMode(pin, mode) configura a arquitetura interna do circuito de entrada/saída através de três modos canônicos:


2. Circuitos de Polarização de Entradas: Pull-Up vs. Pull-Down

Para que uma chave mecânica (push-button) ou sensor digital forneça níveis de tensão unívocos e imunes a espúrios, o circuito deve ser obrigatoriamente referenciado a um potencial fixo quando a chave estiver aberta:

2.1 A Vantagem de Engenharia do INPUT_PULLUP

A ativação do resistor de pull-up interno por firmware (pinMode(pin, INPUT_PULLUP)) dispensa componentes passivos externos na placa de circuito impresso:

  • A chave é conectada diretamente entre o pino do Arduino e a linha de terra (GND);
  • Quando o operador pressiona o botão, o pino é aterrado, resultando na leitura lógica LOW ();
  • Quando o botão é solto, o resistor interno puxa a tensão de volta para , lendo HIGH ().

3. O Repique Mecânico (Contact Bounce) e o Algoritmo de Debounce

Quando os contatos metálicos internos de uma chave mecânica fecham ou abrem, a elasticidade dos materiais condutores faz com que as lâminas colidam e vibrem durante um intervalo de .

Durante essa transição mecânica, a tensão elétrica oscila dezenas de vezes entre e . Como a UCP do ATmega328P executa de instruções por segundo, um único toque humano de botão seria erroneamente contabilizado como pulsos consecutivos!

3.1 Eliminação do Repique com a Função Não-Bloqueante millis()

A função de temporização do sistema millis() retorna o número acumulado de milissegundos decorridos desde que a placa foi ligada através de um contador de 32 bits alimentado por interrupções do Timer0. Ela atinge estouro (overflow) apenas após de operação ininterrupta.

O algoritmo profissional de Debounce Não-Bloqueante com Detecção de Borda implementa-se sem paralisar a UCP:

const uint8_t PINO_BOTAO = 2;
const uint8_t PINO_LED = 13;
 
uint8_t estado_botao_anterior = HIGH;
uint8_t estado_led = LOW;
unsigned long ultimo_tempo_debounce = 0;
const unsigned long DELAY_DEBOUNCE = 50; // Janela de filtro de 50 ms
 
void setup() {
    pinMode(PINO_BOTAO, INPUT_PULLUP);
    pinMode(PINO_LED, OUTPUT);
    digitalWrite(PINO_LED, estado_led);
}
 
void loop() {
    uint8_t leitura_atual = digitalRead(PINO_BOTAO);
    
    // 1. Detecta transição de borda (Mudança de estado no pino físico)
    if (leitura_atual != estado_botao_anterior) {
        ultimo_tempo_debounce = millis(); // Reinicia o cronômetro de filtro
    }
    
    // 2. Se a leitura permaneceu estável por tempo superior à janela de repique:
    if ((millis() - ultimo_tempo_debounce) > DELAY_DEBOUNCE) {
        // Se a tecla foi efetivamente pressionada (borda de descida em Active-Low):
        static uint8_t estado_estabilizado = HIGH;
        if (leitura_atual != estado_estabilizado) {
            estado_estabilizado = leitura_atual;
            if (estado_estabilizado == LOW) {
                estado_led = !estado_led;          // Inverte o estado do LED (Toggle)
                digitalWrite(PINO_LED, estado_led);
            }
        }
    }
    estado_botao_anterior = leitura_atual;
}

4. Modulação por Largura de Pulso (PWM) no Arduino Uno

O microcontrolador ATmega328P não possui conversores digital-analógicos (DAC) verdadeiros em seus pinos de saída. Para produzir tensões contínuas variáveis capazes de regular o brilho de lâmpadas ou a velocidade de motores, emprega-se a Modulação por Largura de Pulso (PWM - Pulse Width Modulation).

A função padrão da API do Arduino é:

Onde valor é um número inteiro de 8 bits variando de (saída permanentemente em , de Duty Cycle) até (saída permanentemente em , de Duty Cycle).

4.1 Cálculo da Tensão Média Contínua ()

A tensão média equivalente aplicada à carga corresponde à área sob a curva do pulso retangular dividida pelo período total :

Para uma carga com constante de tempo elétrica ou mecânica muito superior ao período do sinal PWM (, como o enrolamento indutivo de um motor ou a inércia térmica de um filamento), a carga responde puramente ao valor médio .


5. Mapeamento de Pinos PWM e Frequências dos Temporizadores

Na placa Arduino Uno R3, apenas 6 pinos digitais possuem hardware conectado aos temporizadores internos para geração autônoma de PWM (indicados na serigrafia da placa pelo símbolo til ~):

Pinos PWM ArduinoTemporizador AVR AssociadoModo de Operação PWMFrequência de Chaveamento ()Aplicações Recomendadas
D5 e D6Timer0 (8 bits)Fast PWMDimerização de LEDs (Sem flicker visível)
D9 e D10Timer1 (16 bits)Phase-Correct PWMControle de velocidade de motores CC
D3 e D11Timer2 (8 bits)Phase-Correct PWMCargas térmicas e drivers de potência

IMPORTANT

Conflitos Críticos entre Bibliotecas e Timers:

  1. O Timer0 também gera a base de tempo para as funções vitais millis(), micros() e delay(). Alterar o prescaler do Timer0 descalibrará todos os relógios de software da placa!
  2. A biblioteca padrão de servo motores <Servo.h> assume o controle exclusivo do Timer1. Portanto, ao invocar meu_servo.attach(pin), o PWM nativo por hardware dos pinos D9 e D10 deixa de funcionar!
  3. A função de geração de áudio tone() utiliza o Timer2, desabilitando o PWM dos pinos D3 e D11.

6. Diagrama Arquitetural Consolidado

O diagrama técnico vetorial a seguir sintetiza a topologia física de entradas digitais com chave push-button e resistor de pull-up, o comparativo temporal do repique mecânico contra o filtro de software com millis() e o conjunto de formas de onda PWM com cálculo das frequências de chaveamento de e :

GPIO, Debounce e PWM


7. Exercícios Resolvidos e Aplicações em Concursos

Exercício 1 (Petrobras - Engenharia Eletrônica / CESGRANRIO)

Enunciado: Deseja-se controlar a potência térmica de uma resistência de aquecimento em um processo de secagem laboratorial através de um sinal PWM gerado pelo pino digital D6 de uma placa Arduino Uno. O sistema de controle proporcional calcula que o processo requer exatamente de potência ativa média entregue à carga.

  1. Calcule o valor inteiro de 8 bits a ser fornecido como parâmetro para a função analogWrite(6, valor).
  2. Determine a tensão média contínua entregue à resistência de aquecimento ().
  3. Calcule o período exato em microssegundos do sinal PWM gerado no pino D6 e o tempo em que o sinal permanece em nível lógico alto () durante cada ciclo.

Resolução Passo a Passo:

  1. Cálculo do Parâmetro de 8 Bits: Como a resolução de 8 bits possui passos discretos de fundo de escala: O comando executado no sketch é analogWrite(6, 173).

  2. Cálculo da Tensão Média Real: Com o valor inteiro : Tensão média resultante:

  3. Período e Tempo em Nível Alto (): O pino D6 é controlado pelo Timer0 operando em modo Fast PWM com prescaler de 64: Período total do sinal: Tempo em nível alto ():


Exercício 2 (Marinha do Brasil - Corpo de Engenheiros)

Enunciado: Analise tecnicamente os impactos sobre a arquitetura de um sistema microcontrolado em tempo real ao substituir o controle temporal baseado na função delay() por uma máquina de estados finitos estruturada com a função millis(). Demonstre por que a função millis() não sofre descalibração ou deriva quando executada concorrentemente com outras tarefas no laço loop().

Resolução Técnica Estruturada:

  1. Impactos da Substituição de delay() por millis():
    • Eliminação do Bloqueio da CPU (Non-Blocking Execution): A função delay(N) força o microcontrolador a rodar em um loop estéril de contagem de ciclos por milissegundos, impedindo a amostragem de qualquer outro pino de entrada, recepção de comandos de parada de emergência ou atualização de displays;
    • Viabilização de Pseudo-Multitarefa Cooperativa: Com millis(), o processador compara a marca temporal atual com marcas armazenadas em variáveis estáticas (if (millis() - t_ant >= delta_t)). Se o tempo ainda não venceu, a UCP salta a rotina em fração de microssegundo e executa imediatamente outras dezenas de rotinas no mesmo ciclo do loop(), atingindo operação paralela virtual.
  2. Imunidade a Deriva (Drift) e Independência de Software:
    • A contagem de millis() não depende do tempo de execução do código de software: ela é incrementada por uma Rotina de Serviço de Interrupção de Hardware do Timer0 (TIMER0_OVF_vect) disparada a cada estouro do temporizador (a cada );
    • O hardware do Timer0 conta pulsos de cristal de quartzo independentemente de o software estar ocupado com cálculos matemáticos complexos ou ocioso;
    • Um algoritmo de correção fracionária embutido na ISR do núcleo Arduino compensa o desvio residual de adicionando periodicamente uma unidade à variável global contadora de milissegundos, mantendo a precisão temporal idêntica à precisão física do cristal de quartzo piezoelétrico.