Hace tiempo que quiero construirme un portátil/cyberdeck alrededor de una Raspberry Pi 4, y basado en el diseño del Z88 de Sinclair/Cambridge Computers. Sin embargo, algo que tengo muy claro es que quiero un teclado completo, no uno de portátil, pues, como programador, suelo utilizar mucho las teclas SUPR, INICIO, FIN, RePAG y AvPAG, y en los portátiles algunas necesitan combinarse con Fn.
La primera opción sería utilizar un diseño TenKeyless, también conocido como «teclado 87%», como éste (pero sin lucecitas):
Sin embargo, hay mucho espacio desaprovechado. Así que sopesé utilizar un diseño «75%», como éste de Keychron:
Pero no tenía a mano la tecla de Imprimir pantalla, que uso mucho, ni tampoco la de SysReq, que utilizo de vez en cuando, así que tampoco me convencía.
Al final, después de mucho pensarlo, me decidí por hacer mi propio diseño, y ésta es la distribución final que decidí:
Como se puede ver, tiene todas las teclas de un Tenkeyless, pero es muchísimo más compacto. E incluso tiene una tecla de Fn para funciones no tan comunes, como subir y bajar el volumen. Todo ventajas.
Decidí construir uno en plan «barato», y para ello utilicé pulsadores de circuito impreso y los capuchones de un teclado que compré en el chino el bazar de al lado de casa, y lo conecté directamente a los pines de E/S de la raspberry. Este fue el resultado:
La distribución era buena, pero el tacto… en fin… hace unos treinta años había construido un teclado para mi Spectrum utilizando el mismo tipo de pulsadores, y no recordaba que fuese tan malo 🤣
Pero ya tenía el gusanillo, así que decidí coger el toro por los cuernos y diseñar un teclado en condiciones. Para ello partí de un diseño básico creado mediante el programa Klepcbgen, de Jeroen Bouwens, al que hice varios cambios para permitir, entre otras cosas, utilizar diodos normales en lugar de la versión de montaje superficial. A partir de ahí seguí puliendo el diseño hasta llegar a la versión 1.5 de mi teclado, que está disponible en mi repositorio de gitlab.
En el diseño utilicé un Atmega32U4, el mismo que se utiliza en las placas Teensy 2.0. El motivo es que son las placas que más se utilizan para diseños de teclados propios, por lo que podría reutilizar mucho software. Además, incluí, como se puede ver en la imagen de arriba, dos filas de 10 pads cada una en donde están disponibles las filas y columnas del teclado. Esto permite conectar la misma placa a un microcontrolador externo (o, en mi caso, directamente a los pines de E/S de la Raspberry PI) en lugar de utilizar el incorporado en la placa.
La primera dificultad fue donde colocar el microcontrolador. Al principio junté todas las teclas de la primera fila hacia la izquierda y coloqué el microcontrolador y el conector USB arriba a la derecha, por el lado inferior de la placa. Esto tenía la ventaja de que las pistas del par diferencial del USB eran lo más cortas posible, y además me dejaba sitio para un NeoPixel en la esquina superior derecha, pero tenía el inconveniente de que la estética de la colocación de las teclas no era muy allá, además de que, por el espacio disponible, la placa sobresalía bastante por arriba.
Ante esto, decidí intentar mover el microcontrolador a otra zona, en este caso debajo de la barra espaciadora, donde había mucho espacio libre, y así poder mover el conector USB un poco más hacia las teclas. Sin embargo, me preocupaba que las pistas del par diferencial serían muy largas… ¿daría problemas con el USB?
Decidí informarme, así que me descargué la especificación del estándar USB 1.1 y descubrí que el par diferencial tiene que tener una impedancia de 90 ohmios +/- 15%. Ante esto, utilicé la herramienta de cálculo de Kicad para líneas de transmisión, y me salió que tenía que utilizar una anchura de pista de 0,9 milímetros (sí, bastante ancha):
De todas formas, mi intención era utilizar el modo low-speed, de 1,5Mbps en lugar del full-speed de 12Mbps, por lo que lo más probable es que incluso unas pistas sin ajustar funcionasen correctamente; pero decidí no arriesgarme e intentar hacer las cosas bien. Aquí, además, conté con la inestimable ayuda de mis compañeros de A Industriosa, que me resolvieron todas mis dudas sobre diseño con Kicad. A fin de cuentas, soy programador, no electrónico.
Para hacer bien un par diferencial, lo primero es definir una malla específica para él en las clases de red. De esta manera podremos estar seguros de que los parámetros de las pistas serán correctos:
Para trazar el par, tenemos que utilizar la herramienta específica. Ella nos permite asegurarnos de que ambas pistas están siempre a la distancia correcta:
Una vez trazado el par, veremos que los ángulos son rectos (de 45 o 90 grados), cosa que no es buena. Para solucionar esto sólo tenemos que escoger dos segmentos consecutivos, darle al botón derecho, y escoger pistas de filete. Nos preguntará un valor para el radio, y una vez aceptado, añadirá una esquina curva entre las dos pistas. Aquí tenemos un ejemplo de una esquina ya con el filete, y la esquina paralela aún sin él:
Yo utilicé un radio de 3 milímetros para las curvas internas. Sin embargo, es importante recordar que las curvas deben ser concéntricas, lo que significa que las curvas externas tienen que tener un radio mayor. Dicho radio debe ser radio_interior + ancho_de_pista + separación_de_pista. En mi caso, con un radio interior de 3 milímetros, un ancho de pista de 0,9 milímetros, y una separación de pista de 0,2 milímetros, el resultado es que para los radios exteriores tengo que utilizar un radio de 4,1 milímetros. Y así queda:
Sin embargo, si seleccionamos cada una de las dos pistas del par y nos fijamos en el parámetro Longitud enrutada de cada una, veremos que la longitud no es la misma. Aunque la diferencia es pequeña (casi dos milímetros), quise hacer las cosas bien, así que utilicé la herramienta de Afinar desvío de un par diferencial para asegurarme de que la diferencia de distancias estuviese por debajo de una décima de milímetro:
Esta herramienta añade unas ondulaciones en la pista, de manera que la distancia sea correcta a la vez que se mantiene la impedancia:
Distribuciones de teclado
Durante todo el diseño inicial mantuve una distribución ISO de las teclas, pues es la que yo voy a utilizar. Sin embargo, dado que iba a publicarlo todo bajo una licencia libre, decidí intentar hacer una placa dual, que permitiese montar también una distribución ANSI de teclas. Sin embargo, me encontré con el problema de que había un par de agujeros que quedaban bastante cerca y no veía claro que los estabilizadores que iban en ellos quedasen bien anclados, por lo que ese diseño no parecía una buena idea. Pese a todo, quería poder tener ambas distribuciones, así que al final se me ocurrió una idea relativamente sencilla: en main tendría la placa dual, y luego tendría dos ramas, una para cada distribución, que mantendría actualizadas a partir de main. Al principio era bastante peñazo porque, al quitar los pulsadores que sobraban, quedaban muchas pistas mal situadas, lo que me obligaba a ajustar bastante ambos diseños antes de poder hacer el merge; pero después de afinar la placa dual conseguí solucionar esos problemas, y ahora me lleva segundos actualizar cada rama.
Otros detalles que añadí fueron un conjunto de pads para un programador de Atmega (ese grupo de dos por tres que se ven junto al microcontrolador), dos pads conectados a PE2 y masa para que, al cortocircuitarlos, se pueda entrar en el modo de carga del bootloader de Atmega (que son esos dos que se ven entre el pulsador del espacio y el microcontrolador, y que está etiquetado por debajo como bootloader), y dos pads más para dos pines de E/S que me quedaban libres (PE6 y PB0), pues puede ser interesante aprovecharlos. Teniendo en cuenta que PB1, PB2 y PB3 ya están disponibles en el conector del programador, el resultado es que casi todos los pines de E/S del microcontrolador están accesibles a través de pads. La única excepción es PB7, que se quedó tan encerrado que no encontré una manera razonable de extraer una pista. Es cierto que podría hacer sitio eliminando C7, el condensador de reset, pero la verdad es que no me parece tan necesario poder acceder a ese pin. Ese condensador lo añadí únicamente por si acaso, pero realmente no es buena idea ponerlo pues, al generar un reset automático al enchufar la placa, impide aprovechar la característica de entrar en modo depuración tras un reset del bootloader.
También incluí un rectángulo en blanco en cada lado de la placa, lo que permite hacer anotaciones con un rotulador.
Protección contra sobretensiones
Al principio no tuve en cuenta la necesidad de proteger contra sobretensiones en el bus USB. A fin de cuentas, aunque en la documentación del Atmega32U4 dicen que es recomendable añadirla, no aparecía en ninguno de los diseños de referencia que incluían. Es más: la propia placa Teensy 2.0 no incluye ningún tipo de protección, sino que los dos pines del microcontrolador van a pelo al conector.
Sin embargo, un compañero de AIndustriosa me recomendó encarecidamente que la añadiese, pues parece ser que es habitual que en los conectores USB de los equipos de sobremesa (cuya alimentación va directa a la fuente) haya picos de tensión en determinadas circunstancias. Le eché un vistazo a las opciones que me daba Kicad, y me encontré con que casi todas eran diminutas: algunos chips medían aproximadamente un milímetro de largo… algo imposible de soldar a mano. Afortunadamente había otras opciones, y al final me quedé con el USB6B1, pues además de estar pensado específicamente para USB, tiene un patillaje que simplifica el diseño.
Reloj, o no reloj… he ahí la cuestión
Cuando estaba haciendo el diseño original descubrí que el Atmega32U4 incluye un oscilador RC que viene calibrado de fábrica de manera que su precisión es suficiente para que el USB funcione en modo low-speed. Dado que meter un cuarzo de 16MHz me asustaba bastante (es una frecuencia relativamente alta, y yo no soy para nada un experto en diseño electrónico), decidí que sería mejor hacer un sistema sin cuarzo externo, y utilizar sólo el oscilador RC interno. Sin embargo, a la vez, temía que hubiese algún problema, así que al final decidí que no usaría cuarzo, pero dejaría hechas las pistas para poder poner uno de ser necesario.
¡Menos mal que lo hice! Cuando monté la primera placa y quise programar el micro, descubrí que no funcionaba. Al principio pensé que podía ser un problema con el programador que estaba utilizando, un Bus Pirate, pero después de probar y probar una y otra vez, y de leerme la documentación repetidamente, descubrí que la programación mediante el puerto SPI (que es lo que estaba usando) necesita que haya un reloj activo para funcionar. Pero para que el chip utilizase el RC interno necesitaba programar los fuses de configuración, para lo cual… necesitaba un reloj activo… El huevo y la gallina. Fue aquí cuando descubrí que se vendían dos modelos: el Atmega32U4 a secas, que viene configurado de fábrica para trabajar con un cuarzo externo, y el Atmega32U4RC, idéntico en absolutamente todo excepto en que, de fábrica, viene configurado para trabajar con el oscilador RC interno. Adivinad cual había encargado yo…
Decidí no desesperarme y buscar una solución, así que intenté utilizar la interfaz JTAG para programarlo, confiando en que esa no necesitase un reloj externo. A fin de cuentas, el Bus Pirate es un dispositivo programable, y soporta todo tipo de interfaces…
O no, porque como como estaban justos de espacio de código en el microcontrolador, en las últimas revisiones eliminaron el soporte de JTAG. A fin de cuentas, «era demasiado lento para ser útil» 😡
Afortunadamente había un fork del firmware del Bus Pirate mantenido por la comunidad que, supuestamente, incluía soporte de JTAG, así que lo descargué, lo compilé con el MPlabX, lo flasheé… y no había JTAG.
Revisé el código y ahí estaba… y además se estaba compilando en el binario que estaba grabando, pero no aparecía en la lista de protocolos soportados. No entendía qué estaba pasando.
Encima, leyendo bien la documentación descubrí que el oscilador RC está calibrado para una tensión de trabajo de 3 voltios, cuando yo estaba alimentando el Atmega con 5. Eso me obligaba a recalibrarlo antes de poder usarlo.
Al final me cansé, me fui a la tienda de electrónica de mi ciudad y me compré un cuarzo de 16MHz por 60 míseros céntimos de euro, lo soldé, y se acabaron mis problemas: el teclado aparece como un dispositivo OpenMoko (porque, además, antes del cuarzo de 16MHz le puse, por error, uno de 12MHz pensando que era esa la frecuencia correcta, en base a los 12Mbps del bus USB; pero no…), y como no aparecía el dispositivo, probé a grabar un bootloader diferente del que trae de fábrica: UdaBoot.
Moraleja: no compensa ahorrarse unos céntimos.
¿Y ahora?
Ahora me queda la parte de programar el micro para que se comporte como un teclado, además de ver cómo puedo publicar a través de HID el NeoPixel, de manera que sea fácilmente controlable mediante software. Una vez hecho esto, montaré dos teclados más, y diseñaré la carcasa para el portátil, y una carcasa independiente para poder utilizar este teclado en mis ordenadores de casa.