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:
| Entrada activa | Y2 Y1 Y0 | Qué bits están en 1 |
|---|---|---|
| I0 | 0 0 0 | ninguno |
| I1 | 0 0 1 | Y0 |
| I2 | 0 1 0 | Y1 |
| I3 | 0 1 1 | Y1, Y0 |
| I4 | 1 0 0 | Y2 |
| I5 | 1 0 1 | Y2, Y0 |
| I6 | 1 1 0 | Y2, Y1 |
| I7 | 1 1 1 | Y2, 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.
| I3 I2 I1 I0 | Y1 Y0 | V (válido) | Quién gana |
|---|---|---|---|
| 1 X X X | 1 1 | 1 | I3, sin importar el resto |
| 0 1 X X | 1 0 | 1 | I2 |
| 0 0 1 X | 0 1 | 1 | I1 |
| 0 0 0 1 | 0 0 | 1 | I0 (única activa) |
| 0 0 0 0 | 0 0 | 0 | nadie |
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:
| Versión | Ecuaciones | Puertas | Qué garantiza |
|---|---|---|---|
| Simple | Y1 = I3 + I2 · Y0 = I3 + I1 | 2 OR | Solo una entrada activa |
| Con prioridad y V | Y1 = I3 + I2 · Y0 = I3 + ~I2·I1 · V = OR de 4 | 3 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.
| Entrada activa de mayor índice | ~A2 ~A1 ~A0 | Código real | GS | EO |
|---|---|---|---|---|
| I7 | 0 0 0 | 7 | 0 | 1 |
| I6 (I7 inactiva) | 0 0 1 | 6 | 0 | 1 |
| I5 | 0 1 0 | 5 | 0 | 1 |
| I4 | 0 1 1 | 4 | 0 | 1 |
| I3 | 1 0 0 | 3 | 0 | 1 |
| I2 | 1 0 1 | 2 | 0 | 1 |
| I1 | 1 1 0 | 1 | 0 | 1 |
| Solo I0 | 1 1 1 | 0 | 0 | 1 |
| Ninguna | 1 1 1 | — | 1 | 0 |
| EI = 1 (deshabilitado) | 1 1 1 | — | 1 | 1 |
- 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.
| Pin | Señal | Pin | Señal |
|---|---|---|---|
| 1 | ~I4 | 16 | VCC |
| 2 | ~I5 | 15 | EO |
| 3 | ~I6 | 14 | GS |
| 4 | ~I7 | 13 | ~I3 |
| 5 | EI | 12 | ~I2 |
| 6 | ~A2 | 11 | ~I1 |
| 7 | ~A1 | 10 | ~I0 |
| 8 | GND | 9 | ~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.
| Conexión | Chip alto (I8 a I15) | Chip bajo (I0 a I7) |
|---|---|---|
| EI | GND (siempre habilitado) | EO del chip alto |
| Cuando está activo | Si tiene alguna entrada en 0 | Solo si el alto no tiene ninguna (su EO = 0) |
| Bit 3 del resultado | GS del chip alto (0 = hay entrada alta = bit 3 en 1) | |
| Bits 2 a 0 | AND de las salidas ~A de los dos chips (el chip deshabilitado entrega 111, que no estorba) | |
| GS total | AND 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.
| Teclado | Teclas | Cables directos | Cables en matriz |
|---|---|---|---|
| 3 × 4 (teléfono) | 12 | 12 | 7 |
| 4 × 4 | 16 | 16 | 8 |
| 8 × 8 | 64 | 64 | 16 |
| 8 × 16 (teclado de PC clásico) | 128 | 128 | 24 |
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, enlog2entero 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.
Codificador de 8 a 3
Prioridad · I5 gana
Con prioridad, gana la entrada de mayor índice.
Recorrido guiado (8 minutos)
- 1Mirá 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».
- 2Pasá 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.
- 3Con 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.
- 4Apagá 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í.
- 5Pasá 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.
- 6Apretá «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.
- 7Activá «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.
- 8Probá «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
- Con el simple, activá I1 e I2: ¿qué número sale y por qué es falso?
- ¿Qué diferencia hay entre «ninguna activa» y «solo I0» en cada modo?
- Con el 148, ¿por qué «ninguna activa» y «solo I0» dan los mismos ~A pero distinto GS?
- ¿Cuántas de las 28 parejas posibles de entradas dan un número falso en el simple?
Ver respuestas sugeridas
- 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.
- En el simple, son idénticos (000, indistinguibles). Con prioridad, GS (o V) los separa: «solo I0» tiene GS activo; «ninguna», no.
- 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.
- 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
| En software | En el codificador |
|---|---|
Guardar un enum (3 bits) en lugar de 8 booleanos | Un 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 CPU | Un codificador de prioridad dentro del procesador |
Un planificador con prioridades (nice) | El 148: los de índice alto pasan primero |
| Inversión o hambruna de prioridad | Una IRQ alta que nunca deja pasar a las bajas |
| El patrón cadena de responsabilidad | La cascada EO → EI: cada chip atiende o pasa el turno |
| Una máscara de interrupciones | Ignorar 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 teclas | El ghosting de la matriz sin diodos |
- 1Encoder = compresión.
De ocho booleanos a un número de tres bits: menos pines, la misma información (si hay una sola activa).
- 2Prioridad =
max.De varios candidatos, uno solo gana; el criterio es el índice. Los demás esperan.
- 3GS = 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.