Sistemas combinacionais: a álgebra booleana que sustenta toda lógica digital
Quando você olha uma planta industrial moderna em operação, uma linha de montagem automotiva, uma elevatória de esgoto, uma subestação automatizada, a engenharia de controle parece invisível. Válvulas abrem, motores partem, esteiras andam, tudo no instante exato, sem ninguém apertando nada. Parece mágica.
Mas se você olha o código que faz isso acontecer, a mágica costuma acabar rápido.
Existe um vício bem espalhado no chão de fábrica: puxar linha no ensaio e erro. No calor do comissionamento, com a máquina parada e o cliente no pé, o programador vai inserindo contato em Ladder até a coisa funcionar. Sem equação, sem tabela verdade, sem a menor ideia do que separa um sistema combinacional de um sequencial. Funciona? Funciona. Até o dia em que não funciona, e aí ninguém entende por quê.
O resultado é sempre o mesmo: código frágil, bug intermitente que “só acontece às vezes”, e um programa que ninguém mais consegue dar manutenção sem medo de parar a planta.
A diferença entre esse código e um código de engenharia não é talento nem anos de estrada. É uma base matemática que cabe em um post.
Tudo começa em dois estados
Sistema digital só conhece dois valores:
- 0: baixo, falso, desligado, 0 V ou próximo disso
- 1: alto, verdadeiro, ligado, nível de tensão positivo
Essa dualidade é a base da álgebra booleana, formalizada por George Boole em 1854, quase um século antes de existir computador pra usar ela. Diferente de um sinal analógico, que assume infinitos valores contínuos, o sinal digital só existe em dois níveis bem definidos.
E é exatamente isso que dá robustez a ruído, uma propriedade que não é luxo em ambiente industrial. Um inversor de frequência chaveando a três metros do cabo de sinal injeta lixo elétrico em tudo. Se o sinal fosse contínuo, cada interferência viraria erro de leitura. Como ele é discreto, o ruído precisa ser grande o suficiente pra atravessar a faixa inteira entre 0 e 1 antes de causar estrago.
Níveis lógicos na prática
| Domínio | 0 lógico | 1 lógico | Onde aparece |
|---|---|---|---|
| TTL clássico | 0–0,8 V | 2–5 V | Eletrônica digital |
| CMOS moderno | 0–30 % Vcc | 70–100 % Vcc | Microcontroladores |
| Automação industrial | 0–5 V | 11–30 V | Entradas de CLP (24 V) |
A faixa de transição entre 0 e 1 é o nível de disparo (threshold). Em automação, entradas optoacopladas e filtros de entrada tornam essa transição ainda mais à prova de ruído, e isolam galvanicamente o campo do processador, o que já salvou muita CPU de surto.
Portas lógicas: os átomos da lógica combinacional
Uma porta lógica é um circuito que implementa uma operação booleana elementar. A saída dela depende exclusivamente das entradas naquele instante. Não existe memória, não existe passado.
Essa é a definição central de sistema combinacional:
A saída, em qualquer instante, é função apenas das entradas naquele mesmo instante.
As sete portas que você vai usar pelo resto da carreira. Decore as tabelas verdade, elas aparecem no Ladder, no ST, no FBD e no esquema elétrico.
As três primitivas são AND, OR e NOT. A partir delas se constrói qualquer função lógica concebível. É o que a teoria chama de completeza funcional.
Na prática, trabalhamos também com as derivadas:
- NAND: universal, qualquer circuito pode ser feito só com NANDs
- NOR: também universal
- XOR: saída 1 só quando as entradas são diferentes
- XNOR: complemento do XOR, 1 quando as entradas são iguais
Em circuito integrado, essas portas são transistores operando como chave (corte e saturação). Em CLP, elas são instruções de software executadas pelo processador. A abstração lógica é idêntica nos dois casos, e é por isso que o que você aprende em eletrônica digital vale direto no chão de fábrica.
Álgebra booleana: as regras do jogo
Assim como a álgebra elementar tem propriedades, a booleana tem um conjunto de leis que permitem simplificar expressão antes de ela virar código.
| Lei | Forma OR | Forma AND |
|---|---|---|
| Identidade | A + 0 = A | A · 1 = A |
| Anulação | A + 1 = 1 | A · 0 = 0 |
| Idempotência | A + A = A | A · A = A |
| Complemento | A + Ā = 1 | A · Ā = 0 |
| Distributiva | A + (B·C) = (A+B)·(A+C) | A · (B+C) = A·B + A·C |
| De Morgan | ¬(A + B) = Ā · B̄ | ¬(A · B) = Ā + B̄ |
De Morgan é a mais útil do dia a dia e quase ninguém usa conscientemente. Ela diz que “nenhum alarme ativo” é a mesma coisa que “alarme 1 inativo E alarme 2 inativo E alarme 3 inativo”. Toda vez que você escreve uma linha de permissivo com contatos normalmente fechados em série, está aplicando De Morgan sem saber.
De Morgan na prática: quatro contatos NF em série. A saída AUX_COND[0].7 só cai quando qualquer uma das falhas aparece. Esse bit normalmente alimenta um interlock: enquanto estiver em 1, ele bloqueia a próxima etapa do processo ou dispara o alarme, até a falha de encoder ser corrigida. Lendo ao contrário: Erro = ¬(sem falha 1 · sem falha 2 · sem falha 3 · sem falha 4).
Da tabela verdade à equação: Soma de Produtos
O caminho profissional não é “ir puxando contato até acertar”. É:
- Escrever a tabela verdade a partir da especificação de processo
- Extrair a equação em Soma de Produtos (SOP) ou Produto de Somas (POS)
- Minimizar
- Só então implementar
Na SOP, você pega cada linha da tabela cuja saída é 1 e escreve o produto (AND) das entradas naquele estado, somando (OR) todos esses termos. É mecânico, não tem chute.
Pega um caso real: uma bomba de drenagem que deve ligar quando o nível está alto (A), desde que não haja falha (B), ou quando o operador força em modo manual (C), também sem falha.
| # | A (nível alto) | B (falha) | C (manual) | Y (liga bomba) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 1 | 1 ← |
| 2 | 0 | 1 | 0 | 0 |
| 3 | 0 | 1 | 1 | 0 |
| 4 | 1 | 0 | 0 | 1 ← |
| 5 | 1 | 0 | 1 | 1 ← |
| 6 | 1 | 1 | 0 | 0 |
| 7 | 1 | 1 | 1 | 0 |
Três linhas dão saída 1. Cada uma vira um produto:
1
2
3
4
5
linha 1 → Ā · B̄ · C
linha 4 → A · B̄ · C̄
linha 5 → A · B̄ · C
Y = Ā·B̄·C + A·B̄·C̄ + A·B̄·C (soma de produtos canônica)
Note que B̄ aparece nos três termos: é fator comum. Colocando em evidência:
1
2
Y = B̄ · (Ā·C + A·C̄ + A·C)
Y = B̄ · (A + C) ← forma mínima
De nove contatos para três. “Liga se (nível alto OU manual) E não houver falha”, que é exatamente o que o processo pedia desde o começo, mas agora você provou em vez de achar.
A mesma mecânica aparece em painel real, só que com uma SOP ainda mais direta. Numa planta de mineração, o sentido de giro de um conjunto de esteiras é redundado em dois inversores de frequência, INV1 e INV2. O bit Funcionando frente só pode subir em dois cenários: inversor 1 rodando com o sentido de giro ativo, ou inversor 2 rodando com o sentido de giro ativo. São dois produtos, somados:
1
Funcionando_Frente = (INV1.Running · INV1.DirActive) + (INV2.Running · INV2.DirActive)
Nenhuma simplificação a fazer aqui, os dois termos já são mínimos, mas é a mesma leitura mecânica de tabela verdade: cada inversor rodando no sentido certo é uma linha que dá saída 1, e a soma dos produtos vira a equação.
Soma de produtos desenhada em Ladder. Cada ramo paralelo é um produto (AND de contatos em série); o paralelismo entre eles é a soma (OR). AUX_TRA[0].11 = (INV1.Running · INV1.DirActive) + (INV2.Running · INV2.DirActive).
Repare que a linha de baixo é a mesma estrutura com os contatos de direção negados. É a função complementar: Funcionando_Tras sai da mesma tabela verdade, só invertendo o sentido ativo em cada inversor, sem nenhuma adivinhação.
Minimização: K-map e o raciocínio que sobra
Para funções de até 4–6 variáveis existem os Mapas de Karnaugh: uma grade onde você distribui os 1s da tabela verdade em código Gray e agrupa células vizinhas em blocos de tamanho 1, 2, 4, 8… sempre potência de dois. Cada grupo elimina uma variável; quanto maior o grupo, menos variável sobra nele.
Isso não é curiosidade acadêmica. A maioria dos intertravamentos e permissivos de automação tem entre três e cinco variáveis relevantes, exatamente a faixa onde o K-map funciona de verdade: você pega a especificação, monta a tabela, desenha o mapa, agrupa e sai a equação mínima, pronta pra virar Ladder ou ST. É um método de trabalho real, não só um exercício de prova.
Veja a mesma função Y da bomba de drenagem, com as colunas em código Gray (00 · 01 · 11 · 10, só um bit muda por vez):
A \ BC |
00 |
01 |
11 |
10 |
|---|---|---|---|---|
0 |
0 | 1 |
0 | 0 |
1 |
1 |
1 |
0 | 0 |
Os três 1s formam dois grupos de dois, ambos inteiramente dentro de B = 0:
1
2
3
4
grupo vertical coluna BC=01 → A muda, some → B̄·C
grupo horizontal linha A=1 → C muda, some → A·B̄
Y = B̄·C + A·B̄ = B̄·(A + C) ← mesma forma mínima
A simplificação que fiz no olho algumas seções atrás sai sozinha no mapa, sem precisar fatorar nada manualmente.
Quando o painel cresce, com intertravamentos de dez, vinte variáveis amarradas entre si, o K-map para de ser viável: a grade viraria um cubo multidimensional que ninguém desenha numa folha. É aí que entram o Quine-McCluskey e as heurísticas modernas dos sintetizadores de lógica, como o Espresso, ferramenta, não caneta. Mas o raciocínio de minimização continua valendo todo dia, com ou sem mapa desenhado. Menos contato e menos bobina significa código mais legível, scan mais rápido e menos superfície pra erro.
Quando você olha uma linha de Ladder com dezoito contatos e sente que tem algo redundante ali, é minimização que está faltando, mesmo que você resolva no olho em vez de no mapa.
A ponte: como isso vira CLP
Até aqui falamos de circuito físico. Em um CLP a execução funciona diferente, e entender isso muda como você escreve código:
- As entradas físicas (sensores, botoeiras, fins de curso) são lidas e congeladas numa imagem de processo de entrada (PII)
- O programa do usuário é executado ciclicamente, no scan cycle
- Toda lógica que não depende de estado anterior é, em essência, lógica combinacional
- Os resultados vão para a imagem de processo de saída (PIO) e só então chegam aos atuadores
1
2
3
4
5
6
7
8
9
10
11
┌──────────────── UM SCAN CYCLE ────────────────┐
│ │
CAMPO │ ① ② ③ │ CAMPO
───────┼──────────→ ───────────→ ───────────→ ───────┼───────
sensores│ LÊ ENTRADAS EXECUTA O ESCREVE │ atuadores
24 Vdc │ (congela PROGRAMA SAÍDAS │ bobinas
│ em PII) lógica sobre (da PIO) │ válvulas
│ a PII │
│ ▲ │ │
└────────┴───────────────────────────┘ │
repete, 1 a 50 ms típico
Essa leitura congelada no início do ciclo é o que garante que a sua lógica combinacional se comporte como combinacional de verdade: dentro de um scan, as entradas não mudam no meio do caminho.
No Ladder
O Ladder é tradução quase literal de circuito de relé:
- Contatos em série → AND
- Contatos em paralelo → OR
- Contato normalmente fechado → NOT
- Bobina → atribuição da função
1
2
3
4
5
6
7
AND (série) OR (paralelo) NOT (contato NF)
│──┤ ├──┤ ├──( )──│ │──┬─┤ ├─┬──( )──│ │──┤/├──( )──│
A B Y │ │ A │ Y │ A Y
│ └─┤ ├─┘ │
Y = A · B │ B │ Y = Ā
Y = A + B
Isso não é analogia didática: o Ladder é a álgebra booleana desenhada. Quando você entende isso, parar de “puxar linha” vira consequência natural: você passa a desenhar a equação que já tinha na mão.
Veja a prensa de segurança do exemplo mais abaixo, com o ramo de manutenção em paralelo:
1
2
3
4
5
6
7
│──┤ ├──────┤ ├──────┤ ├────────┤ ├────────────────( )──│
│ Botao_Esq Botao_Dir Cortina_Luz Porta_Fechada Solenoide
│ │
│──┤ ├─────────────────────────────────────────┘
│ Modo_Manut
Solenoide = (Botao_Esq · Botao_Dir · Cortina_Luz · Porta_Fechada) + Modo_Manut
O Modo_Manut em paralelo é o OR: libera o solenoide ignorando os quatro permissivos quando a prensa está em manutenção. Isso é só pra ilustrar a porta OR em Ladder, não é como se faz bypass de segurança numa prensa real: cortina de luz e porta são dispositivo de categoria de segurança, e bypass de verdade vive num relé de segurança ou CLP de segurança dedicado, com chave física, monitoramento de tempo e circuito próprio, nunca um bit de modo comum numa OR de lógica padrão. Fora desse modo, é AND puro, e é exatamente esse AND puro que aparece na captura de painel real abaixo, num intertravamento de bomba:
Intertravamento combinacional puro: PermissivosOK = Nível · Pressão · ModoAuto · Térmico. Quatro contatos em série, um AND de quatro entradas. Nenhuma memória envolvida: se qualquer permissivo cai, a bomba é bloqueada no mesmo instante.
Repare no comentário de cabeçalho da linha. A equação booleana escrita ali em cima não é enfeite: é a especificação do que aquela linha faz, e é o que permite outra pessoa auditar o código sem precisar simular na cabeça.
No texto estruturado
Pra quem vem da computação tradicional, o ST (texto estruturado, IEC 61131-3, sintaxe baseada em Pascal) é a forma mais direta de expressar lógica combinacional complexa.
O erro comum é escrever a lógica direto num PROGRAM, com o endereço físico de cada entrada amarrado na própria declaração da variável. Funciona, mas prende a lógica ao hardware: trocar o módulo de slot ou reaproveitar o mesmo intertravamento numa segunda bomba do mesmo padrão exige reescrever tudo. A prática correta separa as duas coisas: a lógica mora num FUNCTION_BLOCK que só enxerga nomes, o mapeamento físico fica isolado no programa principal.
Pegando o intertravamento da Bomba 1 do Ladder acima, o FUNCTION_BLOCK fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FUNCTION_BLOCK FB_PermissivosBomba
VAR_INPUT
nivelSuccaoOK : BOOL; // nível do poço de sucção dentro da faixa
pressaoDescargaOK : BOOL; // sem sobrepressão na linha de descarga
modoAuto : BOOL; // seletora local em Remoto/Automático
termicoOK : BOOL; // relé térmico íntegro, sem trip de sobrecarga
END_VAR
VAR_OUTPUT
permissivosOK : BOOL;
END_VAR
permissivosOK := nivelSuccaoOK AND pressaoDescargaOK AND modoAuto AND termicoOK;
END_FUNCTION_BLOCK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PROGRAM Main
VAR
iNivelSuccaoOK AT %I0.0 : BOOL;
iPressaoDescargaOK AT %I0.1 : BOOL;
iModoAuto AT %I0.2 : BOOL;
iTermicoOK AT %I0.3 : BOOL;
qPermissivosOK AT %Q0.0 : BOOL;
bomba1 : FB_PermissivosBomba;
END_VAR
bomba1(
nivelSuccaoOK := iNivelSuccaoOK,
pressaoDescargaOK := iPressaoDescargaOK,
modoAuto := iModoAuto,
termicoOK := iTermicoOK
);
qPermissivosOK := bomba1.permissivosOK;
END_PROGRAM
A equação booleana continua exatamente a mesma do Ladder, só que agora vive isolada do endereço físico. O FB_PermissivosBomba pode ser instanciado em qualquer bomba com o mesmo padrão de intertravamento (bomba2, bomba3, cada um com seu próprio mapeamento de entradas), testado em simulação sem CLP nenhum ligado, e versionado como uma unidade só. Em intertravamentos com dezenas de variáveis, comuns em painéis de grande porte, essa separação é o que mantém o projeto auditável conforme ele cresce.
Exemplos reais de lógica combinacional pura
- Comando bimanual de segurança: duas botoeiras pressionadas simultaneamente. A base é um AND (a janela de tempo entra depois, e aí já é sequencial)
- Seleção de modo de operação: chave de três posições (Manual / Automático / Manutenção) com decodificação combinacional e intertravamento mútuo
- Permissivos de partida de motor: nível OK · pressão OK · sem falha térmica · seccionadora fechada
- Prioridade de comandos: emergência tem prioridade absoluta sobre automático, que tem sobre manual
- Alarmes combinacionais: nível baixo · bomba ligada
Um controle de nível com dois sensores é o exemplo mais limpo: nível alto desliga a bomba, nível baixo liga. Não importa o que a bomba estava fazendo antes. Não há etapa, não há sequência, é resposta direta e imediata. Combinacional.
No momento em que você adiciona histerese ou um tempo mínimo entre partidas, o sistema deixa de ser combinacional e vira sequencial. E tudo bem, só precisa estar claro na sua cabeça em que lado da linha você está.
Cuidados de engenharia que o ensaio e erro não cobre
-
Glitches e hazards. Atrasos de propagação e ruído eletromagnético geram variações espúrias nas entradas. Filtros de entrada digital configuráveis no hardware do CLP mitigam falsos disparos, e é exatamente isso que causa aquele bug que “só acontece de madrugada quando o compressor liga”.
-
Fail-safe. Em lógica de segurança, a falha de um sensor (um cabo rompido mandando 0) tem que levar a máquina para o estado seguro, nunca para operação perigosa. Por isso permissivo de segurança usa contato normalmente fechado no campo: cabo partido = permissivo ausente = máquina parada.
-
Tempo de propagação. No mundo físico, a saída muda alguns nanossegundos depois da entrada. Em automação industrial isso é irrelevante frente à dinâmica do processo, mas vira problema em alta frequência.
Onde o combinacional acaba
Um sistema puramente combinacional não tem memória. Ele não consegue lembrar que o botão de partida foi pressionado e solto. Por isso ele não:
- Implementa sequências (passo 1 → passo 2 → passo 3)
- Conta eventos
- Temporiza sozinho
- Implementa máquinas de estado
No instante em que você escreve Motor := (Partida OR Motor) AND NOT Parada, aquele Motor do lado direito é realimentação. É memória. Você acabou de sair do mundo combinacional e entrou no sequencial, assunto pra outro post.
Nada disso exige mestrado. Exige escrever a tabela verdade antes de abrir o software de programação. É literalmente a diferença entre “funcionou” e “está correto”, e o pessoal da manutenção que vai pegar a sua planta daqui a três anos sabe muito bem qual dos dois você entregou.
