Electrónica Digital · Tema 22

Codificadores

El decodificador convertía un número en una línea. El codificador hace el viaje de vuelta: entran muchas líneas, una está activa, y sale el número que la identifica. Es lo que traduce «se pulsó la tecla 7» a un código de 3 bits, o «interrumpió el dispositivo 5» a un índice. La parte difícil es qué hacer cuando varias líneas se activan a la vez: ahí aparece la prioridad.

≈ 55 minutos Sin materiales: solo el navegador Incluye simulador y Python

01 · Punto de partida

Ocho líneas, tres cables

Un sistema tiene ocho fuentes de aviso (sensores, botones, periféricos) y un microcontrolador con tres pines libres. Si cada fuente tuviera su cable al micro harían falta ocho pines. Un codificador resuelve el problema: recibe las ocho líneas y entrega un número de 3 bits que dice cuál está activa. El micro lee el número, no las ocho líneas.

Una escena típica. Un teclado de diez teclas numéricas debe enviar el dígito pulsado a un circuito de 4 bits. O ocho dispositivos piden atención por interrupción y la CPU necesita saber a quién atender. O un tanque con tres flotadores debe entregar un nivel de 2 bits. En todos los casos hay muchas señales «una caliente» (one-hot) y se necesita un número compacto.

En software es guardar un enum en vez de ocho booleanos. Pero con una diferencia importante: en un circuito pueden activarse dos entradas a la vez, y el codificador tiene que decidir qué hacer. Un mal diseño devuelve un número que no corresponde a ninguna de las dos.

  • ¿Qué ecuaciones tiene un codificador de 8 líneas a 3 bits?
  • ¿Qué pasa si se activan dos entradas, y cómo lo resuelve la prioridad?
  • ¿Para qué sirven las salidas GS y EO del 74HC148?
  • ¿Cómo se leen un teclado y una matriz de teclas?
  • ¿Dónde aparece un codificador de prioridad en un procesador o en un conversor?

02 · Codificador simple

Tres OR y listo

Un codificador de 2N líneas a N bits es lo inverso del decodificador (Tema 20): la línea activa de índice k debe producir el número binario k. Cada bit de salida vale 1 cuando el índice activo tiene ese bit en 1, así que cada salida es un OR de las entradas cuyo índice lo contiene:

Codificador simple de 8 a 3 (con exactamente una entrada activa).
Entrada activaY2 Y1 Y0Qué bits están en 1
I00 0 0ninguno
I10 0 1Y0
I20 1 0Y1
I30 1 1Y1, Y0
I41 0 0Y2
I51 0 1Y2, Y0
I61 1 0Y2, Y1
I71 1 1Y2, Y1, Y0
Tres OR de cuatro entradas
Un 8 a 3 simple son tres puertas OR de 4 entradas (un 74HC4072 trae dos; hace falta un chip y medio) y nada más. Un 4 a 2 son dos OR de dos entradas: Y1 = I3 + I2 e Y0 = I3 + I1.
I0 no se cablea
La entrada I0 produce el código 000, el mismo que «ninguna entrada activa». No participa de ninguna ecuación. Eso es una ambigüedad: de la salida sola no se puede saber si estaba activa I0 o no estaba activa ninguna.
Decodificar y codificar
Un decodificador seguido de un codificador devuelve el número original. Es la operación inversa, como serializar y deserializar. Se verifica en el Paso 1 de Python.

03 · Prioridad

Cuando hablan varios a la vez

En el mundo real las entradas pueden activarse simultáneamente: dos teclas, dos interrupciones, dos sensores. Un codificador de prioridad asigna un orden: gana la entrada de mayor índice y las demás se ignoran. Así la salida siempre corresponde a una de las entradas realmente activas.

Codificador de prioridad 4 a 2. X = no importa (cualquier valor).
I3 I2 I1 I0Y1 Y0V (válido)Quién gana
1 X X X1 11I3, sin importar el resto
0 1 X X1 01I2
0 0 1 X0 11I1
0 0 0 10 01I0 (única activa)
0 0 0 00 00nadie

Fijate en las X: una tabla de prioridad es una tabla con condiciones de «no importa» (Tema 6), porque cuando gana I3 no interesa qué valen las demás. Eso permite minimizar a mano o con un mapa de Karnaugh:

Costo de un 4 a 2 simple frente a uno con prioridad.
VersiónEcuacionesPuertasQué garantiza
SimpleY1 = I3 + I2 · Y0 = I3 + I12 ORSolo una entrada activa
Con prioridad y VY1 = I3 + I2 · Y0 = I3 + ~I2·I1 · V = OR de 43 OR, 1 NOT, 1 AND (y el OR de V)Cualquier combinación de entradas; V distingue «I0» de «nadie»
La señal V (o GS)
Un codificador de prioridad agrega una salida que vale 1 cuando hay alguna entrada activa. Sin ella, la salida 00 es ambigua (¿I0 o nadie?). Es el equivalente de devolver un valor y una bandera de validez, como un Optional.
La prioridad es numérica
Gana el índice mayor, no la primera señal en llegar ni la «más importante» a ojo. El diseñador decide qué fuente conecta a qué índice: lo más urgente, al número más alto.
Costo y retardo
La prioridad agrega lógica: cada entrada debe «enterarse» de si hay otra de mayor índice activa. En un codificador grande esa cadena puede ser lenta, por eso se usan estructuras en árbol.

04 · 74HC148

GS, EO y la cascada

El 74HC148 es el codificador de prioridad de 8 a 3 de la serie 74HC. Todas sus señales son activas en bajo: las entradas se activan con 0, y las salidas A2 A1 A0 dan el código invertido (el índice 7 sale como 000). Tiene además una entrada de habilitación EI y dos salidas de control, GS y EO.

74HC148 con EI = 0 (habilitado). Las entradas y salidas son activas en bajo. En la columna de entradas se indica la de mayor prioridad activa.
Entrada activa de mayor índice~A2 ~A1 ~A0Código realGSEO
I70 0 0701
I6 (I7 inactiva)0 0 1601
I50 1 0501
I40 1 1401
I31 0 0301
I21 0 1201
I11 1 0101
Solo I01 1 1001
Ninguna1 1 1—10
EI = 1 (deshabilitado)1 1 1—11
GS (group select)
Vale 0 cuando hay alguna entrada activa. Es la señal V de la sección anterior, en lógica negativa. Distingue «solo I0» de «ninguna»: en ambos casos ~A = 111, pero GS vale 0 en el primero y 1 en el segundo.
EO (enable output)
Vale 0 cuando el chip está habilitado y ninguna entrada está activa: avisa «no tengo nada, puede seguir otro». Se conecta a la entrada EI del siguiente chip para armar una cadena.
Lógica negativa por todos lados
Para leer el número real hay que invertir las tres salidas (~A2 ~A1 ~A0 = 010 es el 5). En un microcontrolador, se lee y se complementa por software; en un circuito, se usan inversores o se alimentan displays activos en bajo.
Pines del 74HC148 (16 pines). Confirmalo en la hoja de datos del fabricante.
PinSeñalPinSeñal
1~I416VCC
2~I515EO
3~I614GS
4~I713~I3
5EI12~I2
6~A211~I1
7~A110~I0
8GND9~A0

Cascada: de 8 a 16 entradas

Para 16 entradas se usan dos 74HC148. La regla es la de una fila de ventanillas: el chip de las entradas más altas se atiende primero, y solo si no tiene nada pasa el turno al de las entradas bajas.

Cascada de dos 74HC148 para un codificador de prioridad de 16 a 4.
ConexiónChip alto (I8 a I15)Chip bajo (I0 a I7)
EIGND (siempre habilitado)EO del chip alto
Cuando está activoSi tiene alguna entrada en 0Solo si el alto no tiene ninguna (su EO = 0)
Bit 3 del resultadoGS del chip alto (0 = hay entrada alta = bit 3 en 1)
Bits 2 a 0AND de las salidas ~A de los dos chips (el chip deshabilitado entrega 111, que no estorba)
GS totalAND de los dos GS

Es un patrón que vas a ver más veces: una cadena de responsabilidad hecha de hardware. Cada chip pregunta «¿tengo algo?»; si sí, lo atiende y silencia al siguiente; si no, le pasa el turno.

05 · Teclados

74HC147 y la matriz con barrido

74HC147: decimal a BCD
Un codificador de prioridad con nueve entradas (I1 a I9, activas en bajo) y cuatro salidas BCD invertidas. Diez teclas numéricas (0 a 9) entran por las nueve líneas más una convención: «ninguna tecla» equivale al 0. Con la tecla 5, las salidas dan 1010 (el 0101 complementado). Sin ninguna tecla, 1111 (el 0000 complementado). No tiene GS: detectar «alguna tecla pulsada» exige una puerta aparte (un AND de las nueve entradas).
La matriz de teclas
Un teclado de 16 teclas no tiene 16 cables: son 4 filas por 4 columnas (8 cables). Cada tecla une una fila con una columna. El circuito barre: un decodificador (Tema 20) pone en bajo una fila por vez mientras se leen las columnas; si una columna baja, la tecla de esa fila y esa columna está pulsada. Un codificador (o el microcontrolador) convierte (fila, columna) en un código de 4 bits.
Rebote
Cada tecla rebota (Tema 11). El barrido se hace a velocidad moderada y se exige que la lectura sea la misma en varias pasadas antes de aceptarla.
Cuántos cables y cuántas teclas.
TecladoTeclasCables directosCables en matriz
3 × 4 (teléfono)12127
4 × 416168
8 × 8646416
8 × 16 (teclado de PC clásico)12812824

El problema de los fantasmas (ghosting)

En una matriz sin diodos, si se pulsan tres teclas en forma de «L» (por ejemplo, las posiciones (1,1), (1,2) y (2,1)), la corriente encuentra un camino alternativo: al barrer la fila 2, la columna 2 baja también, porque está unida a la columna 1 a través de la fila 1. El circuito ve una cuarta tecla, la (2,2), que nadie pulsó.

Sin diodos

Fantasmas posibles

Tres teclas en «L» crean una cuarta falsa. Barato y válido si solo se pulsa una tecla a la vez (calculadoras, teclados numéricos).

Con un diodo por tecla

Sin fantasmas

El diodo bloquea el camino de retorno. Cada tecla se ve sola. Es lo que usan los teclados de juego y de PC con «N-key rollover».

06 · Prioridad en el mundo real

Interrupciones, conversores y una instrucción de CPU

Controlador de interrupciones
Ocho dispositivos piden atención por líneas distintas. Un codificador de prioridad elige el de mayor número y entrega su índice a la CPU, que salta a su rutina (el «vector»). Los controladores como el 8259 de los PC clásicos hacen exactamente esto, con una máscara para ignorar dispositivos.
Hambruna (starvation)
Con prioridad fija, si una fuente de alta prioridad pide atención sin parar, las de baja prioridad nunca se atienden. Es el mismo problema que en un planificador de procesos: la prioridad es un compromiso entre urgencia y equidad (Paso 6 de Python).
Convertidor analógico-digital «flash»
El conversor más rápido usa 2N − 1 comparadores, cada uno con un umbral distinto (Tema 1). Para una tensión de entrada, todos los comparadores por debajo valen 1 y los de arriba 0: un código termómetro. Un codificador de prioridad convierte ese termómetro en un número binario: el índice del comparador más alto que está activo.
El tanque con flotadores
Los tres flotadores del Tema 17 son un código termómetro de 3 bits. Un codificador de prioridad da el nivel sin necesidad de marcar las combinaciones imposibles como «no importa»; pero tampoco detecta una falla (un flotador trabado): hay que verificar que el termómetro sea monótono.
Una instrucción de CPU
La instrucción «contar ceros a la izquierda» (clz, count leading zeros) o «buscar el primer bit en 1» (bsr, ffs) es un codificador de prioridad dentro del procesador: dice cuál es el bit más alto que vale 1. Se usa en planificadores, en log2 entero y en asignadores de memoria.

07 · Simulador

Laboratorio: numerar con prioridad

El simulador tiene ocho entradas I0 a I7 (cada botón activa o apaga una fuente) y un codificador de 8 a 3. Podés elegir entre un codificador simple (tres OR) y uno con prioridad, y ver los niveles como se verían en un circuito didáctico (activos en alto) o en un 74HC148 real (activos en bajo, con GS y EO). Con el simple, al activar dos entradas el simulador detecta el número falso que se produce.

  • Líneas de entrada: amarilla = una entrada activa y atendida; naranja = activa pero ignorada por la prioridad; roja = entrada que participa de un conflicto en el codificador simple.
  • Salidas: A2 A1 A0 (y GS, EO en el modo real). En el modo real las salidas son activas en bajo: el número se ve complementado.
  • Resultados: el número de salida, quién gana, las señales de control y un estado que avisa de conflictos y de números falsos.
SIMULADOR 22

Codificador de 8 a 3

Prioridad · I5 gana

Codificador de ocho líneas a tres bits Ocho líneas de entrada I0 a I7, un bloque codificador y las salidas A2, A1 y A0 con las señales de control GS y EO. Las entradas atendidas, ignoradas y en conflicto se dibujan con colores distintos. OCHO LÍNEAS DE ENTRADA, UN NÚMERO DE TRES BITS
Salida (A2 A1 A0)—
Entradas activas y ganadora—
GS y EO—
Estado—

Con prioridad, gana la entrada de mayor índice.

Recorrido guiado (8 minutos)

  1. 1
    Mirá el estado inicial: I3 e I5 activas, con prioridad.

    Gana I5: el código es 101 (el 5). La línea de I3 está en naranja: está activa, pero la prioridad la ignora. GS vale «hay alguna».

  2. 2
    Pasá a «Simple (tres OR)» sin tocar las entradas.

    Con I3 e I5 activas, los OR mezclan: Y2 = 1 (por I5), Y1 = 1 (por I3), Y0 = 1 (por I3 y por I5): sale 111, el código de I7, que nadie activó. El estado avisa de un número falso y las líneas se ponen en rojo.

  3. 3
    Con el simple, dejá una sola entrada con «Una sola, siguiente».

    Con exactamente una, el simple funciona: cada entrada da su número. Es el uso correcto del codificador simple.

  4. 4
    Apagá todo («Ninguna activa»).

    La salida es 000, igual que cuando solo I0 está activa. Probalo: activá solo I0 y comparalo. En el simple no hay forma de distinguirlos; con prioridad, GS (o V) sí.

  5. 5
    Pasá a «74HC148 (activo en bajo)».

    Ahora los pines de las entradas valen 0 cuando están activas y las salidas muestran el código invertido: el 5 aparece como 010. GS vale 0 si hay alguna entrada activa y EO vale 0 solo cuando no hay ninguna.

  6. 6
    Apretá «EI (habilitación)» para deshabilitar el chip.

    Con EI = 1 las salidas A pasan a 111, GS a 1 y EO a 1: el chip se apaga y no «ve» ninguna entrada. EO vale 1, así que tampoco habilita al siguiente de una cascada.

  7. 7
    Activá «Todas activas».

    Con prioridad gana I7 (el código 7, o 000 en pines de 148). Con el simple, los OR dan 111 también, pero por una razón equivocada: es la suma de todas.

  8. 8
    Probá «Dos al azar» varias veces con el simple.

    A veces el número resultante coincide con una de las dos activas (cuando una incluye bit a bit a la otra, como I5 e I1), y a veces no. En las dos situaciones el circuito «parece» funcionar, y ese es el peligro: el fallo es intermitente.

Preguntas para pensar

  1. Con el simple, activá I1 e I2: ¿qué número sale y por qué es falso?
  2. ¿Qué diferencia hay entre «ninguna activa» y «solo I0» en cada modo?
  3. Con el 148, ¿por qué «ninguna activa» y «solo I0» dan los mismos ~A pero distinto GS?
  4. ¿Cuántas de las 28 parejas posibles de entradas dan un número falso en el simple?
Ver respuestas sugeridas
  1. I1 aporta Y0 e I2 aporta Y1: sale 011, el código de I3. Ninguna de las dos activas es la 3: el número es falso.
  2. En el simple, son idénticos (000, indistinguibles). Con prioridad, GS (o V) los separa: «solo I0» tiene GS activo; «ninguna», no.
  3. I0 produce el código 0, que invertido es 111, igual que cuando no hay nada. Lo único que cambia es que hay una entrada activa, y eso lo dice GS.
  4. Son 9 de 28 (el Paso 1 de Python las cuenta): las otras 19 coinciden con una de las dos entradas porque una incluye bit a bit a la otra.

08 · Codificar en código

Numerar, priorizar, escanear y servir interrupciones

Los bloques verifican el codificador simple y cuentan sus números falsos, derivan y verifican las ecuaciones del codificador de prioridad, modelan el 74HC148 completo con su cascada de 16 entradas, reproducen el fantasma de un teclado matricial sin diodos, convierten un código termómetro (conversor flash y tanque) y simulan un controlador de interrupciones con su problema de hambruna.

Python en tu navegador. Los bloques son editables: cambiá las entradas y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

Paso 1: el codificador simple y sus números falsos

from itertools import combinations


def codificar8(i):                 # i: lista de 8 bits; con un solo 1 es correcto
    y2 = i[4] | i[5] | i[6] | i[7]
    y1 = i[2] | i[3] | i[6] | i[7]
    y0 = i[1] | i[3] | i[5] | i[7]
    return 4 * y2 + 2 * y1 + y0


def decodificar8(n):
    return [1 if k == n else 0 for k in range(8)]


print("decodificar y codificar devuelve el mismo número:",
      all(codificar8(decodificar8(n)) == n for n in range(8)))

falsos = total = 0
for a, b in combinations(range(8), 2):
    i = [0] * 8
    i[a] = i[b] = 1
    n = codificar8(i)
    total += 1
    if n not in (a, b):
        falsos += 1
print(f"con dos entradas activas: {falsos} de {total} parejas dan un número que no es ninguna de las dos")

i = [0] * 8
i[1] = i[2] = 1
print("I1 e I2 activas -> el codificador dice", codificar8(i), "(es el código de I3)")
print("ninguna activa ->", codificar8([0] * 8), "= lo mismo que I0 sola:", codificar8(decodificar8(0)))

Con una entrada el circuito funciona para las 8. Con dos, 9 de las 28 parejas producen un número que no corresponde a ninguna; las otras 19 coinciden por casualidad (una incluye a la otra bit a bit). «Ninguna» e «I0» son indistinguibles.

Paso 2: prioridad con ecuaciones

from itertools import product


def prioridad4(i3, i2, i1, i0):
    y1 = i3 | i2
    y0 = i3 | ((1 - i2) & i1)
    v = i3 | i2 | i1 | i0
    return y1, y0, v


def referencia(i3, i2, i1, i0):
    activos = [k for k, x in zip((3, 2, 1, 0), (i3, i2, i1, i0)) if x]
    if not activos:
        return 0, 0, 0
    g = max(activos)
    return g >> 1, g & 1, 1


ok = all(prioridad4(*b) == referencia(*b) for b in product((0, 1), repeat=4))
print("las ecuaciones Y1 = I3+I2, Y0 = I3+~I2·I1, V = OR coinciden con la regla «gana el mayor»:", ok)
print()
print("I3 I2 I1 I0 | Y1 Y0 V")
for b in product((0, 1), repeat=4):
    y1, y0, v = prioridad4(*b)
    print(f" {b[0]}  {b[1]}  {b[2]}  {b[3]}  |  {y1}  {y0}  {v}")

Las tres ecuaciones reproducen la prioridad en las 16 combinaciones: gana el mayor, y V es 1 salvo con todas las entradas apagadas. El caso «0 0 0 0» y el «0 0 0 1» dan el mismo Y pero distinto V.

Paso 3: el 74HC148 y la cascada de 16 entradas

import random


def hc148(i_n, ei_n=0):
    # i_n: 8 valores (índice 0..7), activos en bajo. Devuelve (a2_n, a1_n, a0_n, gs_n, eo_n)
    if ei_n == 1:
        return 1, 1, 1, 1, 1
    activos = [k for k in range(8) if i_n[k] == 0]
    if not activos:
        return 1, 1, 1, 1, 0
    g = max(activos)
    return 1 - ((g >> 2) & 1), 1 - ((g >> 1) & 1), 1 - (g & 1), 0, 1


print("entrada ganadora   ~A2 ~A1 ~A0  GS EO")
for g in (7, 6, 5, 3, 0):
    i_n = [0 if k == g else 1 for k in range(8)]
    a2, a1, a0, gs, eo = hc148(i_n)
    print(f"      I{g}            {a2}   {a1}   {a0}    {gs}  {eo}")
print("ninguna            ", *hc148([1] * 8))
print("EI = 1             ", *hc148([0] * 8, ei_n=1))


def prioridad16(activas):
    alto = [0 if (8 + k) in activas else 1 for k in range(8)]
    bajo = [0 if k in activas else 1 for k in range(8)]
    a = hc148(alto, ei_n=0)
    b = hc148(bajo, ei_n=a[4])                 # EO del chip alto -> EI del chip bajo
    a2, a1, a0 = a[0] & b[0], a[1] & b[1], a[2] & b[2]
    bit3 = 1 - a[3]                            # GS del chip alto en lógica negativa
    gs_n = a[3] & b[3]
    return None if gs_n else bit3 * 8 + (1 - a2) * 4 + (1 - a1) * 2 + (1 - a0)


ok = all(prioridad16({k}) == k for k in range(16))
rnd = random.Random(2)
for _ in range(500):
    s = {k for k in range(16) if rnd.random() < 0.2}
    ok &= prioridad16(s) == (max(s) if s else None)
print("cascada de dos 148 = codificador de prioridad de 16 entradas:", ok)

Con I7: ~A = 000, GS = 0 y EO = 1. Con ninguna: ~A = 111, GS = 1 y EO = 0 (habilita al siguiente). Con I0 sola: ~A = 111 pero GS = 0. La cascada, con EO del alto en el EI del bajo y los resultados combinados con AND, coincide con «gana el mayor de 16» en 500 casos al azar.

Paso 4: el fantasma de un teclado matricial

from collections import deque


def alcanzables(origen, teclas):
    grafo = {}
    for r, c in teclas:
        grafo.setdefault(("R", r), []).append(("C", c))
        grafo.setdefault(("C", c), []).append(("R", r))
    vistos, cola = {origen}, deque([origen])
    while cola:
        n = cola.popleft()
        for m in grafo.get(n, []):
            if m not in vistos:
                vistos.add(m)
                cola.append(m)
    return vistos


def leer_columnas(r, teclas, con_diodos):
    if con_diodos:                                  # el diodo bloquea el camino de retorno
        return sorted(c for rr, c in teclas if rr == r)
    return sorted(n[1] for n in alcanzables(("R", r), teclas) if n[0] == "C")


def escanear(teclas, con_diodos):
    return [(r, c) for r in range(4) for c in leer_columnas(r, teclas, con_diodos)]


def codigo(rc):
    return rc[0] * 4 + rc[1]                        # fila y columna -> código de 4 bits


for pulsadas in ({(1, 1), (1, 2), (2, 1)}, {(0, 0), (0, 1), (1, 0)}, {(1, 1), (2, 2)}):
    sin = escanear(pulsadas, False)
    con = escanear(pulsadas, True)
    fantasmas = sorted(set(sin) - set(pulsadas))
    print(f"pulsadas {sorted(pulsadas)}")
    print(f"   sin diodos lee {sin}  fantasmas: {fantasmas}")
    print(f"   con diodos lee {con}  códigos: {[codigo(x) for x in con]}")

Tres teclas en «L» crean una cuarta falsa: con (1,1), (1,2) y (2,1) pulsadas, el barrido sin diodos ve también la (2,2). Dos teclas en diagonal no producen fantasmas. Con un diodo por tecla, la lectura siempre coincide con lo pulsado.

Paso 5: código termómetro, conversor flash y tanque

VREF, N = 5.0, 3
LSB = VREF / 2 ** N


def comparadores(vin):
    return [1 if vin > k * LSB else 0 for k in range(1, 2 ** N)]      # 7 comparadores


def codificar_termometro(t):
    return max([k + 1 for k, x in enumerate(t) if x], default=0)       # prioridad: el más alto activo


print(f"conversor flash de {N} bits, Vref = {VREF} V, LSB = {LSB:.3f} V")
for vin in (0.0, 0.5, 1.3, 2.6, 3.9, 4.99):
    t = comparadores(vin)
    c = codificar_termometro(t)
    print(f"{vin:4.2f} V  termómetro = {''.join(map(str, t))}  código = {c} ({c:03b})  esperado = {int(vin / LSB)}")

print()
print("tanque: S1 S2 S3 (bajo, medio, alto) -> nivel y validez")
for t in ((0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (0, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1)):
    nivel = codificar_termometro(list(t))
    valido = all(t[k] >= t[k + 1] for k in range(2))
    print(f"   {t}  nivel = {nivel}  {'válido' if valido else 'termómetro roto: FALLA'}")

El conversor flash numera sus 7 comparadores con un codificador de prioridad: 3,9 V es 111111 0 → 6. El tanque da un nivel aun con combinaciones imposibles (010 daría 2), pero el chequeo de monotonía del termómetro las detecta como falla.

Paso 6: interrupciones, prioridad y hambruna

def servir(llegadas, pasos, mascara=frozenset()):
    pendientes = set()
    log = []
    for t in range(pasos):
        pendientes.update(llegadas.get(t, []))
        candidatas = pendientes - set(mascara)
        if candidatas:
            g = max(candidatas)                  # el codificador de prioridad elige el mayor
            pendientes.discard(g)
            log.append(g)
        else:
            log.append(None)
    return log, sorted(pendientes)


# la IRQ 7 llega en cada paso durante 10 pasos; la IRQ 2 llega en el paso 0
llegadas = {t: [7] for t in range(10)}
llegadas[0] = [7, 2]
log, pend = servir(llegadas, 14)
print("atendidas:", log)
print("pendientes al final:", pend)
print("la IRQ 2 se atendió recién en el paso", log.index(2), "(después de que la 7 dejó de pedir)")

log2, pend2 = servir(llegadas, 14, mascara={7})
print()
print("con la IRQ 7 enmascarada:", log2)

Con la IRQ 7 pidiendo en cada paso, la IRQ 2 queda pendiente 10 pasos (hambruna) y solo se atiende cuando la 7 deja de llegar. Enmascarar la 7 la deja pasar. La prioridad fija es simple pero injusta con las fuentes de menor prioridad.

09 · Analogía de programador

Un árbitro con niveles

El mismo concepto, en dos mundos.
En softwareEn el codificador
Guardar un enum (3 bits) en lugar de 8 booleanosUn codificador: de uno entre N a binario
max(indices_activos)Un codificador de prioridad: gana el mayor índice
Un Optional: valor y bandera de «hay valor»La salida y la señal GS/V: distinguir «cero» de «nadie»
Instrucciones clz / bsr / ffs de la CPUUn codificador de prioridad dentro del procesador
Un planificador con prioridades (nice)El 148: los de índice alto pasan primero
Inversión o hambruna de prioridadUna IRQ alta que nunca deja pasar a las bajas
El patrón cadena de responsabilidadLa cascada EO → EI: cada chip atiende o pasa el turno
Una máscara de interrupcionesIgnorar una entrada del codificador
Un histograma convertido en un índice (cuántos umbrales se superaron)El código termómetro del conversor flash
Lecturas «rasgadas» de un teclado de N teclasEl ghosting de la matriz sin diodos
  1. 1
    Encoder = compresión.

    De ocho booleanos a un número de tres bits: menos pines, la misma información (si hay una sola activa).

  2. 2
    Prioridad = max.

    De varios candidatos, uno solo gana; el criterio es el índice. Los demás esperan.

  3. 3
    GS = bandera de validez.

    Distinguir «el valor es cero» de «no hay valor», siempre explícito.

Una diferencia con el software: en un programa, max([]) lanza un error o devuelve None. En el hardware, un codificador sin entradas activas sigue entregando un número (000 o 111) que el resto del circuito puede confundir con un dato. Por eso la señal de validez no es un lujo: es la manera de que «nadie» no se lea como «I0».

10 · Comprensión

Confusiones frecuentes

«Dos entradas activas dan igual»

En el codificador simple, los OR mezclan los bits y salen números falsos (I1 e I2 dan el código de I3). O se garantiza una sola entrada activa, o se usa prioridad.

«Gana la primera que se activó»

Gana la de mayor índice, no la primera en llegar ni la que parezca más importante. La prioridad es numérica y fija; el diseñador asigna las fuentes a los índices.

«GS es opcional»

Sin GS (o V), «nadie» y «solo I0» son indistinguibles. La bandera de validez forma parte del dato.

«Codificador y decodificador son lo mismo»

Son inversos: el decodificador convierte un número en una línea; el codificador, una línea en un número. Uno detrás del otro se anulan.

«El 148 entrega el número directamente»

Sus salidas son activas en bajo: entrega el complemento. El 5 sale como 010. Hay que invertir (o leerlo complementado por software).

«En la cascada, el chip bajo manda sobre el alto»

Es al revés: el EO del chip de mayor prioridad va al EI del de menor. El de arriba se atiende primero y solo cede el turno cuando no tiene nada.

«Una matriz de teclas sin diodos detecta cualquier combinación»

Con tres teclas en «L» aparece una cuarta falsa (ghosting). Para detectar combinaciones hace falta un diodo por tecla.

11 · Práctica guiada

Ejercicios de numerar

Ejercicio 1: 4 a 2 con OR

Armá un codificador simple de 4 a 2 con un 74HC32: Y1 = I3 + I2, Y0 = I3 + I1. ¿Cuántas puertas del chip sobran? ¿A dónde va I0? ¿Cómo se detectaría «ninguna activa»?

Ver solución razonada

Dos OR: sobran dos de las cuatro del chip (sus entradas se atan a GND). I0 no se cablea (su código es 00). Para saber si hay alguna activa se necesita una señal aparte: V = I0 + I1 + I2 + I3 (OR de cuatro entradas, o una NOR si se quiere activa en bajo).

Ejercicio 2: teclado 0 a 9 con el 74HC147

Diez teclas llegan a un 74HC147 (entradas activas en bajo, salidas BCD complementadas). ¿Qué salida da la tecla 5? ¿Y ninguna tecla? ¿Cómo se distingue «tecla 0» de «ninguna»?

Ver solución razonada

La tecla 5 da 1010 en los pines (0101 complementado). Ninguna tecla da 1111 (0000 complementado). El 147 no tiene I0 ni GS: el 0 es «sin tecla», así que «tecla 0» y «ninguna» dan lo mismo. Para distinguirlas hay que detectar «alguna tecla pulsada» aparte, con un AND de las nueve entradas (en lógica negativa, esa salida vale 0 si hay alguna).

Ejercicio 3: cascada de 16 a 4

Dos 74HC148 forman un codificador de 16 entradas. Si solo está activa la entrada global 11 (I3 del chip alto), ¿qué salidas tiene cada chip, cuál es el resultado y qué pasa con el chip bajo?

Ver solución razonada

El chip alto tiene I3 en 0: sus salidas ~A = 100, GS = 0 y EO = 1. Como su EO es 1, el chip bajo queda deshabilitado (EI = 1): sus salidas ~A = 111. El AND de ambas da 100 en los pines; invertido, 011. El bit 3 sale del GS del alto (0 en bajo, es decir, 1): el código es 1011 = 11. Verificalo con el Paso 3.

Ejercicio 4: derivar la prioridad

Con la tabla de prioridad 4 a 2 (con X), derivá Y1 e Y0 con un mapa de Karnaugh de 4 variables. ¿Cuántas puertas necesitás?

Ver solución razonada

Y1 vale 1 cuando I3 o I2 están activas (las filas con I3 = 1 o con I3 = 0, I2 = 1): Y1 = I3 + I2. Y0 vale 1 cuando gana I3 o gana I1: I3 = 1, o I3 = 0, I2 = 0 e I1 = 1; con las X se escribe Y0 = I3 + ~I2·I1. V = OR de las cuatro. Puertas: 1 OR (Y1), 1 NOT, 1 AND y 1 OR (Y0) y un OR de cuatro entradas (V): cinco puertas en total.

Ejercicio 5: fantasmas en una matriz

En un teclado matricial sin diodos se pulsan las teclas (0,0), (0,1) y (1,0). ¿Qué cuarta tecla aparece y por qué? ¿Cómo se evita?

Ver solución razonada

Aparece la (1,1): al barrer la fila 1, la columna 1 baja a través de la fila 0 (unida a la columna 0 por la tecla (1,0) y a la columna 1 por la (0,1)). Se evita con un diodo en serie con cada tecla, que bloquea el camino de retorno, o limitando el sistema a una sola tecla a la vez.

Ejercicio 6: un conversor flash de 3 bits

Un conversor flash de 3 bits con Vref = 5 V. ¿Cuántos comparadores hacen falta? ¿Cuál es el código termómetro y el binario para 3,9 V?

Ver solución razonada

23 − 1 = 7 comparadores, con umbrales cada 0,625 V (el LSB). Para 3,9 V superan el umbral los seis primeros (0,625 a 3,75 V), no el séptimo (4,375 V): termómetro 1111110 y código 6 (110). Un codificador de prioridad toma el comparador activo más alto (el sexto) y entrega su número.

12 · Síntesis

Ideas para recordar

  • Un codificador convierte una línea activa entre 2N en un número de N bits. El simple son N puertas OR: Y0 = I1+I3+I5+I7 en el 8 a 3.
  • El simple exige exactamente una entrada activa: con dos, los OR mezclan los bits y salen números falsos. «Ninguna» e I0 dan el mismo código.
  • El codificador de prioridad elige el mayor índice activo. Su tabla tiene X, y se minimiza (Y1 = I3 + I2, Y0 = I3 + ~I2·I1). Agrega la bandera V.
  • 74HC148: 8 a 3 con prioridad, todo activo en bajo (las salidas son el complemento del índice). GS = 0 si hay alguna entrada activa; EO = 0 si está habilitado y no hay ninguna.
  • Cascada: EO del chip alto al EI del bajo; el bit alto sale del GS del alto y el resto del AND de las salidas.
  • 74HC147: decimal a BCD, sin GS; el 0 es «ninguna tecla». Matriz de teclas: barrido con un decodificador y lectura de columnas; sin diodos hay fantasmas.
  • Aplicaciones: controlador de interrupciones (con riesgo de hambruna), conversor flash con código termómetro, instrucciones clz/bsr.
  • Codificador y decodificador son inversos. La bandera de validez distingue «cero» de «nadie».

¿Listo para el Tema 23?

Podés avanzar si sos capaz de responder esto sin mirar:

  • Escribir las tres ecuaciones del codificador simple de 8 a 3.
  • Explicar qué sale del codificador simple si se activan I1 e I2, y por qué es un problema.
  • Derivar la tabla del codificador de prioridad 4 a 2 y sus ecuaciones.
  • Decir qué valen ~A, GS y EO del 74HC148 con ninguna entrada activa y con solo I0.
  • Indicar cómo se conectan dos 148 para 16 entradas.
  • Explicar el fantasma de una matriz de teclas sin diodos.

Ya sabés convertir líneas en números y números en líneas. Con esas herramientas llega la aritmética: en el próximo tema, el sumador medio y completo, donde la lógica combinacional empieza a calcular.