Este é o oitavo curso de Engenharia Elétrica Universitária: microprocessadores
e sistemas embutidos. EE03 construiu a lógica; este curso
constrói o máquina que executa programas e a disciplina de criar hardware
e software se encontram.
A Unidade 8.1 mapeia a arquitetura: CPU, registradores, memória e o
ciclo busca-decodificação-execução.
As Unidades 8.2–8.3 programam-na: C embutido e assembly, seguidos de E/S com endereçamento de memória.
As Unidades 8.4–8.5 adicionam tempo: interrupções/temporizadores e protocolos seriais.
As Unidades 8.6–8.7 encerram com interfaces analógicas e métodos de depuração.
A história em andamento é um pequeno produto: um nó sensor alimentado por bateria com um
botão, um LED, uma ligação serial e um sensor de 12 bits. Cada unidade abaixo avança esse
nó, e cada número é verificado da forma como um engenheiro de firmware verifica
— por larguras e limites de bits (o valor cabe?), pelo relógio que ele assume
(todo cronograma fecha contra 84 MHz), e rastreamos as exatas mudanças de estado
de cada fragmento de código.
Por que importa. O termostato na parede, o controlador de freios antipatinagem
e o fone de ouvido na mesa executam cada um deles: um computador completo em um
chip do tamanho de um grão de arroz, custando menos que o frete para enviá-lo.
A ideia, com uma imagem. Um microcontrolador integra três
coisas em um único chip: memória flash armazenando o programa, SRAM armazenando os dados,
e uma CPU que executa o ciclo busca-decodificação-execução: o
contador de programa (PC) armazena o endereço da próxima instrução,
a unidade de controle decodifica-a, a unidade de dados a executa, e o PC avança (ou
salta). Toda transferência ocorre no ** barramento** — linhas de endereço indicando onde,
linhas de dados transportando o quê, linhas de controle indicando leitura ou escrita — sequenciadas pelo
relógio.
Uma instrução por ciclo: buscar no PC, decodificar, executar, avançar
A derivação. Endereços são inteiros sem sinal, portanto um campo de endereço de N bits
endereça $2^{N}$ locais exatamente. O mapa de memória inteiro segue:
Flash em 0x0000_0000 e SRAM em 0x2000_0000 nunca colidem porque seus
bits superiores diferem — o decodificador de endereços apenas verifica os bits [31:16].
Exemplo resolvido 1 — dimensionando o mapa de memória. O chip do nó possui 64 KB de flash
e 8 KB de SRAM. Flash: $64\,\text{KB} = 2^{16}$ bytes → 16 linhas de endereço, ocupando
o intervalo 0x0000_0000–0x0000_FFFF. SRAM: $8\,\text{KB} = 2^{13}$ bytes →
13 linhas, no intervalo 0x2000_0000–0x2000_1FFF. Verifique: qualquer endereço com bits
[31:16] = 0x0000 and [15:0] ≤ 0xFFFF lands in flash; 0x2000_… lands in
SRAM; as regiões não se sobrepõem porque seus bits superiores diferem ✓.
Exemplo resolvido 2 — velocidade do próprio laço? A CPU do nó opera a
84 MHz com nominalmente uma instrução por ciclo (um núcleo da classe Cortex-M):
$84 \times 10^{6}$ instruções por segundo, logo uma instruição leva
$\dfrac{1}{84 \times 10^{6}} = 11.9$ ns. Uma passagem de controle de 100 instruções
custa 1.19 µs — e um laço de controle de 1 ms comporta cerca de 840
passagens se nada mais interferir. A aritmética orçamentária iniciada aqui é
reutilizada em todas as unidades subsequentes.
Erros comuns. Misturar bits de endereço com bits de dados (as linhas de endereço
contam locais, as linhas de dados definem sua largura). Esquecer que o mapa é
fixado no script de linkagem: o mesmo chip inicializa em qualquer endereço que o
fornecedor de hardware escolheu. E citar o tamanho do programa em bits quando os tamanhos de flash são
em bytes — um fator de oito que define a escolha do componente.
Resumo. Um único chip: flash + SRAM + CPU ciclando busca, decodificação e execução. Um
Campo de endereço N-bits endereça $2^{N}$ bytes; regiões com bits superiores distintos
não podem se sobrepor; o clock converte a contagem de instruções em segundos.
Compare the worked 64 KiB flash and 8 KiB SRAM regions. Move from the last mapped byte to the next address, separate the base from the offset bits, and explore why 96 KiB needs 17 offset bits but leaves 32 KiB unused in its address window.
Por que importa. O firmware do nó é algumas milhares de linhas de C tocando
registros de hardware diretamente. Nada entre sua linha de código e um
transistor — que é exatamente por isso que o mapeamento de C para máquina deve ser
preciso.
A ideia, com uma imagem. Um registrador mapeado em memória
é apenas um endereço; o C acessa-o através de um volatile
ponteiro (volatile avisa ao compilador: esta memória muda fora da sua visão,
acessa-a toda vez). O compilador transforma C em cargas, armazenamentos, aritmética e
ramificações; assembly é o mesmo programa escrito manualmente naquelas instruções
uma por uma, uma linha cada.
Uma linha de C, uma sequência de instruções, uma transferência de registrador — três visões de um único ato
A derivação. Ler-modificar-escrita é o ato fundamental: para alterar um
pino sem perturbar seus vizinhos, a CPU deve carregar o registro, mascarar
o bit localmente e armazená-lo novamente. Isso são três operações, mas apenas duas
transferências de barramento: uma leitura e uma escrita. Este exemplo usa uma leitura/escrita simples
este exemplo simples de registrador de palavra usa load-mask-store. Rastreie o estado em papel em hexadecimal antes dos
volatile uint32_t *const GPIO_OUT = (uint32_t *)0x40000000u;
*GPIO_OUT |= (1u << 5); // load, OR, store — set bit 5 only
Exemplo resolvido 1 — rastreie o contador. O loop C
Uma implementação ilustrativa de carga-incremento-armazenamento mais ramificação rastreia
$0, 1, 2, 3, \ldots$. Um compilador pode, em vez disso, manter este contador não volátil
em um registrador da CPU. Verifique o limite: o maior valor é $2^{32} - 1 = 4\,294\,967\,295$;
o próximo incremento volta a zero após $2^{32}$ incrementos a partir de zero.
Assumindo quatro ciclos de clock por iteração a 84 MHz, o wrap leva
$\dfrac{4\,294\,967\,296 \times 4}{84 \times 10^{6}} \approx 204$ s —
aritmética, não espera três minutos com cronômetro.
Exemplo resolvido 2 — a máscara de bits, escalonada. Para alternar bits 5 e 6
e limpar o bit 3 em uma única expressão: a máscara é
$(1 \ll 5) \,|\, (1 \ll 6) = 0x60$. Então *GPIO_OUT = (*GPIO_OUT & ~0x08) ^ 0x60 limpa bit 3 e inverte 5 e 6 em uma leitura-modificação-escrita. Verifique no
papel com GPIO_OUT = 0xCE: 0xCE & ~0x08 = 0xC6, então 0xC6 ^ 0x60 = 0xA6 — o bit 3 agora está 0 (estava 1), os bits 6 e 5 foram invertidos de 1, 0 para 0, 1 ✓. A verificação hexa em
papel é o depurador mais rápido nesta unidade.
Erros comuns. Remover volatile (o compilador armazena o registrador em
registrador da CPU e seu loop o lê para sempre). Assumir ordem de avaliação C
entre duas gravações de registradores quando o hardware os precisa em sequência — insira
uma barreira ou leitura de retorno. E escrever bit 5 como *GPIO_OUT = 0x20, o que limpa
todos os outros bits na porta.
Resumo. Registradores são endereços; volatile mantém o compilador honesto;
este exemplo simples de palavra-registrador usa carga-máscara-armazenamento. Rastreie o estado no papel em hex antes
confiar no hardware.
Start with the worked low byte 0xCE, clear bit 3 and toggle bits 5 and 6 to get 0xC6 then 0xA6. Step through the load, local CPU operations and store; the GPIO word stays unchanged until the store. Compare OR-setting bit 5 with plain assignment, and set upper neighbours to see which operations preserve them.
Por que isso importa. O botão, LED e sensor do nó estão todos fora da
CPU. A forma como se conectam determina se o firmware é uma tabela limpa de
endereços ou um amontoado de números mágicos.
A ideia, com uma imagem.E/S mapeada em memória faz um
registro periférico parecer exatamente como uma localização de memória: ler um registro de
dados recebe um byte; escrever envia-o. Uma porta GPIO tem
dois desses registros — direção (entrada ou saída, um bit por pino) e
dados (nível do pino) — para configurar uma vez e depois ler ou escrever indefinidamente.
Direção diz por onde o pino fala; dados dizem o que ele diz
A dedução.Polling é a linha de base honesta: o bit de
concluído do registro de status é uma leitura de memória em um loop. Cada volta custa as
instruções do loop; esperar T segundos consome
$$T \times f_{\text{clock}}$$
ciclos da CPU, ou aproximadamente $T f_{\text{clock}}/C$ voltas completas quando cada
volta custa $C$ ciclos de clock. O fator $C$ altera o número de verificações,
não o tempo ocupado pela espera ativa contínua. Detecção e timeout
testes são quantizados nos tempos de verificação do laço.
Exemplo resolvido 1 — o custo de perguntar. O nó verifica um bit de
sensor pronto em 84 MHz; cada volta do loop leva 4 ciclos (carregar, AND, desvio, incremento), então a
CPU executa $84 \times 10^{6}/4 = 21$ milhões de voltas por segundo. Esperar
1 ms equivale a ~21 000 voltas = 84 000 ciclos: 100% da CPU naquele milissegundo,
com zero trabalho útil.
Exemplo resolvido 2 — mapa de todo o nó. Os periféricos do nó estão localizados em
0x4000_0000 (GPIO), 0x4001_0000 (UART), 0x4002_0000 (timer),
0x4003_0000 (ADC). Uma tabela de constante de firmware mapeia cada driver para sua base;
um erro de digitação em uma entrada (0x4001_0000 vs 0x4001_0004) resulta em quatro bytes dentro do
conjunto de registradores da UART — é por isso que cada arquivo de driver inicia com
offsets nomeados, nunca endereços brutos. A verificação: após a inicialização, leia o
registrador ID de cada periférico; uma base incorreta retorna lixo na primeira leitura.
Erros comuns. Esquecer o registrador de direção (o pino permanece como
entrada e a escrita não tem efeito). Polling sem timeout — um sensor parado
trava todo o produto. E confundir o número de bit de uma porta com o número de
pino no pacote: a tabela de pinos do datasheet, não o macro conveniente do cabeçalho,
é a verdade.
Resumo. Periféricos são memória: base mais offsets nomeados. Configure a
direção uma vez, faça polling com timeout e use uma verificação de leitura ID para provar
o mapa antes de qualquer trabalho real ser executado.
Explorar
Fewer polling checks do not mean a less busy CPU
$cycles=checks\times C,\quad checks\approx T f_{clock}/C,\quad cycles\approx T f_{clock}$
Start with the worked 1 ms wait at 84 MHz and four cycles per polling lap. The loop makes 21000 checks and occupies 84000 cycles. Increase the lap cost to reduce the number of checks without freeing the CPU during the wait. Put readiness between checks to see detection lag, or remove the ready event and watch the timeout stop the wait.
Por que importa. O nó opera com bateria por um ano. Polling consome a
bateria fazendo perguntas; interrupções permitem que a CPU durma até que o mundo precise
dela — a diferença entre uma semana e um ano.
A ideia, com uma imagem. Uma interrupção suspende o programa principal
onde está: o hardware empilha o estado de retorno, vetoriza para uma
rotina de serviço de interrupção (ISR) escolhida pelo NVIC (o
controlador de interrupções aninhado por vetores, que também classifica prioridades), a ISR
executa e retorna. Um timer é apenas um contador sincronizado por um
divisor — o prescaler. No contador ascendente estilo STM32 usado aqui,
um evento de atualização ocorre quando CNT retorna de ARR a zero; uma interrupção habilitada
pode então chamar a ISR.
Duas divisões convertem 84 MHz em uma interrupção de 1 Hz
A dedução. A frequência do timer é dividida duas vezes:
onde PSC e ARR são os valores dos registradores armazenados 16-bit, no máximo 65,535.
Cada fator divisor pode atingir 65,536 porque o zero também é contado. Um projeto
é válido quando os valores armazenados cabem E $PR=f_{\text{clock}}/f_{\text{interrupt}}$.
Aqui ARR é diferente de zero e o contador opera continuamente.
Exemplo resolvido 1 — batimento cardíaco de 1 Hz. De 84 MHz: fator divisor 8400 resulta em
$84 \times 10^{6}/8400 = 10$ kHz; outro fator de 10,000 resulta exatamente em 1 Hz.
Escreva PSC = 8399 e ARR = 9999. Ambos os valores armazenados cabem em 16 bits, e
$8400 \times 10\,000 = 84 \times 10^{6}$ ✓ fecha o projeto. Um divisor
fator de 84,000 exigiria PSC = 83,999, que não cabe.
Exemplo resolvido 2 — latência de interrupção vs polagem, precificado. Uma GPIO
interrupt com 12 ciclos de entrada, uma ISR de 30 instrução e 10 ciclos de
custos de retorno $12 + 30 + 10 = 52$ ciclos $= 0.62$ µs em 84 MHz — contra o
21 000 voltas a espera polida queimou. Mas o custo da ISR é pago em cada
evento: em 1 kHz de eventos isso é 52 000 ciclos/s = 0.06% da CPU; em
1 MHz de eventos é 62% — passado certa taxa, polir um periférico rápido é
mais barato que interromper. A aritmética de transição é a decisão de projeto.
Erros comuns. Uma ISR que esquece de limpar sua flag de interrupção — a
CPU retorna e reentra imediatamente, para sempre. ISRs longas (façam o mínimo,
definir uma bandeira, deixe o loop principal funcionar). E correndo o temporizador: lendo um 16-bit
timer em um barramento de 8-bits precisa do byte alto lido duas vezes ou latchado, ou um carry
entre leituras corrompe o valor.
Resumo. Interrupção = vetor + ISR curta + limpeza de flag; timer =
$(PSC+1)(ARR+1)$ contra o relógio, com ambos os valores armazenados dentro de seus
registros. Interrupções vencem em baixas taxas de evento; poling vence acima de ~1 MHz.
Explorar
The divider factor includes the zero count
$f_{update}=f_{clock}/[(PSC+1)(ARR+1)]$
Start with the worked 84 MHz to 1 Hz heartbeat. Divider factors 8400 and 10000 require stored PSC = 8399 and ARR = 9999 in this STM32-style upcounter. Advance exact clock edges, counter ticks and update events, then use a small 4 × 5 example to see zero counted. Compare writing the factors directly to the registers and observe the changed rate.
interrupt service routine/ˈɪntərʌpt ˈsɜːvɪs ruːˈtiːn/
rotina de serviço de interrupção
prescaler
prescaler
8.5
Protocolos seriais
Por que importa. As leituras do nó saem por três fios ou menos.
Escolher UART, SPI ou I2C — e precificar o fio contra a velocidade — é uma
decisão semanal no trabalho embutido.
A ideia, com uma imagem.UART envia bytes assíncronos:
sem clock compartilhado, apenas um bit de início, 8 bits de dados, um bit de parada, a uma
taxa de baud ambas as extremidades concordaram. SPI compartilha um clock: o mestre aciona SCLK e
chip-select, os dados fluem em MOSI para fora e MISO para dentro — full duplex. I2C
precisa de dois fios (SDA dados, SCL clock), endereça dispositivos com
7 bits, e os fios são open-drain para que qualquer dispositivo possa manter a linha baixa.
Um quadro 8N1: 10 tempos de bit por byte, 86.8 µs a 115 200 baud
A derivação. UART não tem linha de clock, então ambas as extremidades reconstruem o tempo
a partir da baud combinada: um byte no enquadramento 8N1 ocupa exatamente 10 tempos de bit,
SPI envia um bit por ciclo completo de clock, então um byte leva 8 clocks mais
setup: $T_{\text{byte}} \approx 8/f_{\text{SCLK}}$ — o clock é a velocidade.
Exemplo resolvido 1 — dimensionamento UART. A 115 200 baud, 8N1: throughput é
$115\,200/10 = 11\,520$ bytes/s; um byte leva $10/115\,200 = 86.8$ µs.
Contra o loop de controle de 1 ms do nó: 11 bytes cabem (0.95 ms) ✓; uma
explosão bloqueante de 100 bytes (8.68 ms) excede o orçamento — divida ou aumente a taxa de baud. O
dimensionamento decide, não preferência.
Exemplo resolvido 2 — o preço tripartido. SPI a 10 MHz clocka um byte em
$8/10\,\text{MHz} = 0.8$ µs — cerca de 108× mais rápido que UART aqui — mas custa um
fio chip-select por dispositivo. I2C em 400 kHz precisa de 9 clocks para 8 bits de dados mais ACK:
$9/400\,\text{kHz} = 22.5$ µs, antes das sobrecargas de endereço, início e parada.
Nessas taxas escolhidas, situa-se entre SPI e UART, mas dois fios
servem toda a placa (endereçamento de 7-bit = até 112 dispositivos a 1 endereço por
dispositivo). Sensor em I2C, flash em SPI, console em UART — a divisão padrão,
cada um precificado por esta aritmética.
Erros comuns. Baud desajustada (o quadro ainda 'funciona' e os bytes
são lixo — verifique a matemática do divisor contra o clock do periférico, não a
relógio central). Ler um endereço I2C como 8 bits: o endereço de 7-bits mais o
bit de leitura/escrita compõe um byte, e as fichas técnicas citam ambas as formas. E
esquecer o modo SPI (polaridade e fase do relógio): os quatro modos existem porque
os dispositivos diferem; o modo errado desloca cada byte em um bit.
Resumo. UART: 10 tempos de bit por byte, sem clock, ambas as extremidades concordam com a
taxa de baud. SPI: um bit por clock, um fio por dispositivo. I2C: dois fios, endereços de 7-bits, open-drain. Compare o custo em bytes por milissegundo contra os fios antes de
escolher.
Por que importa. O mundo físico fala em voltagem; a CPU fala em números.
Start with the worked 115200-baud link and a repeated 0x41 byte. Read the start, least-significant-first data and stop cells; a 100-byte burst occupies 8.68 ms, while only 11 complete frames fit in 1 ms. Change the receiver baud to move its sampling points and distinguish wrong data, a low stop sample and a sample exactly on a transition.
A leitura de temperatura do nó é tão boa quanto o conversor e sua
fiação — e os erros aqui são silenciosos, não ruidosos.
fiação — e os erros aqui são silenciosos, não altos.
A ideia, com uma imagem. Um ADC quantiza: um conversor de N-bits
que abrange uma tensão de referência $V_{\text{ref}}$ tem uma resolução
de um único LSB$= V_{\text{ref}}/2^{N}$. A escolha de N é
a escolha de quão finamente o mundo é medido; a fiação decide se
essa finura sobrevive.
o contém: código 2048
A derivação. A quantização mapeia uma tensão para o código cuja faixa
contém: código $= \lfloor V_{\text{in}}/V_{\text{ref}} \times 2^{N} \rfloor$
(com variantes nas bordas). Reconstruindo cada faixa de entrada em seu ponto médio
fornece um limite ideal de erro de quantização de metade de um LSB dentro da faixa de entrada; uma entrada individual pode ter erro zero.
faixa; uma entrada individual pode ter erro zero.
Exemplo resolvido 1 — o sensor de 12 bits. Com $V_{\text{ref}} = 3.3$ V, um
LSB equivale a $3.3/4096 = 0.806$ mV. Uma entrada de 1.65 V lê
$1.65/3.3 \times 4096 = 2048$ — exatamente na escala central ✓. Se o nó precisa
resolver 0.1 °C e o sensor fornece 10 mV/°C, ele requer 1 mV de resolução —
um conversor de 12 bits (0.806 mV) atende, mas um de 10 bits (3.22 mV) falha: é o
requisito, não o catálogo, que define N.
Exemplo resolvido 2 — a armadilha da impedância de fonte. O amostrador e segurador do ADC
capacitor ($\approx 7$ pF neste chip) carrega através da resistência da fonte
e assentamento em ½ LSB exige um tempo de amostragem de pelo menos
$R_{\text{source}} C \ln(2^{N+1})$. Para uma fonte de 50 kΩ isso é
($50\,000 \times 7\,\text{pF} \times 9.01 \approx 3.2$ kΩ Thevenin) portanto exige pelo menos uma janela de 1 µs — ou reduza pela metade o divisor de tensão
(impedância de Thevenin de $16$ kΩ) requer então a configuração de 100 µs — ou reduzir pela metade os valores do divisor.
(50 kΩ Thevenin) portanto, precisa de pelo menos uma janela de 3.2 µs — ou reduza à metade o divisor
valores. Um DAC percorre a mesma escada inversamente: ao escrever
2048 em um DAC de 12 bits com referência de $V_{\text{ref}} = 3.3$ V, produz-se 1.650 V.
Erros comuns. Citar resolução em bits e esquecer a referência
(0.806 mV só é válido para 3.3 V). Tratar a entrada do ADC como voltímetro:
suas cargas de amostragem atuais são sensores de alta impedância, conforme o limite RC acima
mostra. E a média para 'comprar' bits — fazer a média 16 amostras de ruído branco
ganha cerca de 2 bits de resolução efetiva, mas não pode reparar um
desvio sistemático ou uma amostra com queda de tensão.
Resumo. Um LSB = $V_{\text{ref}}/2^{N}$; o requisito define N.
A reconstrução do ponto médio fornece um limite de erro de quantização de meio LSB; o limite RC
$t_{s} \ge R C \ln(2^{N+1})$ troca impedância da fonte pelo tempo de amostragem —
a impedância da fonte permitida depende do modelo e da janela de aquisição.
Explorar
Fine ADC steps need time for the sample to settle
$V_{error}=\Delta V e^{-t_s/(RC)},\quad t_s\ge RC\max[0,\ln(2^{N+1}\Delta V/V_{ref})]$
Start with the worked 50 kΩ source and 7 pF sampling capacitor on a 12-bit ADC. A 3.2 µs acquisition window meets the half-LSB settling budget; a 1 µs window misses it. Lower the source resistance or lengthen acquisition, then compare 16 bits to see why finer voltage steps demand a smaller remaining error. Separate acquisition error from quantisation.
Por que importa. O firmware é testado executando-o no hardware exato em que
será comercializado, e cada bug chega como um boato ("congelou duas vezes na
noite passada"). O método faz a diferença entre consertá-lo e renomeá-lo.
A ideia, com uma imagem. O ciclo é: reproduzir com a menor
entrada; bisseccionar a fronteira entre código confiável e suspeito (um LED, um
ponto de interrupção ou uma impressão UART); corrigir UMA coisa; revalidar. Um watchdog
é a rede de segurança que reinicia um sistema travado — acionar o contador antes
que expire, ou o produto reiniciará por projeto em vez de congelar por
acidente.
Reproduzir, bisseccionar, corrigir uma coisa, revalidar — e um watchdog fora do ciclo
A derivação. A bisseccão é o algoritmo: se a falha reproduz com
a sonda de fronteira colocada no ponto médio da faixa suspeita, o erro
vive na primeira metade; caso contrário, na segunda. Cada sonda reduz a faixa pela metade, logo
$\log_{2}(\text{suspicious instructions})$ sondas são suficientes — dez sondas para mil instruções. A mesma bisseção que opera a escada ADC opera o
ciclo de depuração.
loop de depuração.
Exemplo resolvido 1 — desbouncing como traçado de estado. O botão do nó lê
1, 0, 0, 1 em quatro amostras de 10 ms (contato oscilante). Regra: exigir duas
leituras iguais consecutivas. Traçado: t = 0 ms lê 1 (sem veredito); t = 10 ms
lê 0 (uma, sem veredito); t = 20 ms lê 0 novamente → estável 0 declarado.
O valor 1 em t = 30 ms é uma única amostra, ainda instável, e é corretamente ignorado.
de estado: todo o processo em um único traçado.
decisão de estado: todo o curso em um único traçado.
Exemplo resolvido 2 — bisseção da congelamento. O nó congela dentro de uma
hour of a specific log sequence; the suspected region is 1 024 instructions
do driver de comunicação. Faça a instrução 512 acender o LED: se o nó congela
com o LED ainda apagado, o erro está na primeira metade; se o LED acende
antes do congelamento, está na segunda. Cada execução reduz pela metade a região: 512 → 256 → 128 → … — dez execuções delimitam
a uma única instrução. A aritmética explica por que a bisseção supera reler o
código esperando identificá-lo: $2^{10} = 1024$.
Erros comuns. Corrigir duas coisas de uma vez (uma funcionou, outra
quebrou outro componente — agora há três falhas). Depuração em uma placa
diferente da que falhou. E desativando o watchdog "por enquanto":
a falha de campo que o watchdog teria suportado é exatamente a que
ocorre.
Resumo. Reproduza, divida ao meio, corrija um, revereifique; $\log_{2}$ sondas delimitam
a caçada. Debouncing converte ruído em eventos por N amostras consecutivas. O
watchdog é a última barreira: acione-o apenas no loop principal, nunca em ISR.
Explorar
One contrary reading does not erase the qualified state
Step through the worked 1,0,0,1 trace at 10 ms intervals with two equal readings required. The first qualified 0 appears at 20 ms; the later single 1 does not reverse it. Edit samples, compare a one-sample pulse and an alternating input, and change N to distinguish input chatter from accepted output changes. Startup remains unqualified until a state earns enough consecutive readings.