Archivo de la categoría: Sin categoría

Problemas tecladiles (2)

Acabo de subir la revisión 1.7 de la placa del teclado. Uno de los cambios más importantes ha sido conectar una de las columnas del teclado desde el puerto PE2 al PB7. Para entender el motivo, primero necesitamos entender cómo es el proceso de arranque del Atmega32Ux.

El procesador Atmega32Ux permite añadir un bootloader y arrancarlo en determinadas circunstancias. Hay varias posibilidades:

  • Llamarlo manualmente desde el programa principal (útil para cambiar a modo programación de manera «manual»)
  • Llamarlo siempre que se resetee el procesador
  • Llamarlo tras un reset si se da cierta condición externa

Y esta última es la clave: esa condición externa es que el pin PE2 esté a nivel bajo en el momento de ocurrir el reset. Esto permite que, por defecto, se arranque el código principal, pero si se desea arrancar el bootloader, sólo haya que conectar dicho pin a masa y pulsar reset.

Pues bien, hasta ahora yo utilizaba dicho pin para una de las columnas del teclado simplemente por comodidad de diseño: al hacer el enrutado de las pistas, era más sencillo utilizar ese pin que otro.

El problema es que la placa Teensy 2.0, que es la que lleva el mismo procesador, tiene ese pin permanentemente conectado a masa a través de un pull-down y, además, no está conectado al exterior. Eso significa que en las bibliotecas de teensyduino no está disponible la opción de utilizar dicho pin.

Por simplicidad, yo quería mantener la compatibilidad con la Teensy 2.0 porque me da un entorno de programación y bibliotecas listas para utilizar, lo que simplifica mucho el trabajo, pero este problema me obligaba a rediseñar la placa, algo que no me apetecía mucho porque ya mandé fabricarlas y, aunque la versión actual es mucho más avanzada que la que tengo físicamente, los cambios hechos hasta ahora eran cosméticos y no afectaban a la lógica en sí, mientras que éste sí implicaba un cambio en la lógica de funcionamiento.

Decidí ver qué alternativas tenía, y descubrí que las placas Arduino Leonardo y Arduino Micro también utilizaban el mismo micro, así que decidí echar un vistazo a ver si podía utilizarlas. Al principio tenía buena pinta porque el bootloader estaba disponible, por lo que podía convertir mi teclado en un Arduino «con todas las de la ley». Por desgracia, la alegría no duró mucho: en el caso del Micro, el pin PE2 tampoco estaba conectado al exterior, con lo que estaba en las mismas. Aún peor: otros dos de los pines que utilizo en el teclado están asignados a LEDs, con lo que probablemente no tendría mucha libertad a la hora de utilizarlos. En el caso del Leonardo, tres cuartos de lo mismo.

Al final no me quedó otra que cortar la pista que conectaba PE2, y añadir dos puentes: uno entre PB7 y la pista original, y otra entre masa y una resistencia conectada a PE2.

Probando otros bootloaders

A pesar de todo, decidí probar el bootloader de Arduino, por si era más cómodo que el ubaboot que estaba utilizando hasta ese momento. Arranqué el editor Arduino IDE, configuré una placa Leonardo, le indiqué que tenía un programador Bus Pirate, y le di a Quemar bootloader. Varios minutos (sí, sí… tarda un muy buen rato y no muestra nada en pantalla que indique progreso) finalizó la grabación. Lo primero que descubrí es que me había cambiado los fuses del Atmega, algo que no me gustó mucho porque algunos flags eran… raros. Por ejemplo, me activaba el divisor entre ocho del cuarzo, lo que supone que la CPU va más lenta. Pero lo más raro es que pone a cero los bits 4 y 5 del byte extendido, los cuales se supone que no se deben escribir, y deben estar siempre a 1. Lo peor es que no soy capaz de devolverlos a su estado normal.

Tras varias pruebas, no me acabó de convencer el bootloader, así que volví al ubaboot de nuevo, pero esta vez quería asegurarme de corregir un par de problemas que tenía, y es que al pulsar el botón de reset para entrar en modo programación, no siempre funcionaba, sino que normalmente tenía que darle dos o tres veces antes de que el identificador USB fuese el de OpenMoko en lugar del de Teensy. Por otro lado, al darle la orden de reiniciar desde el programador ubaboot, el USB dejaba de funcionar.

Al final, tras echar un vistazo al código fuente, me di cuenta de que este bootloader espera ser llamado siempre durante el arranque, lo que significa que hay que poner a cero el bit 0 del Fuse High Byte, el flag BOOTRST. De esta manera, siempre arrancará desde el bootloader, y si éste detecta que el procesador está arrancando por haber sido conectado a la corriente, saltará directamente al código principal, y sólo entrará en modo bootloader si se pulsó el botón de reset.

Con estos cambios ya puedo programar fácilmente la placa del teclado: simplemente tengo que instalar Teensyduino para disponer de todas las bibliotecas, indicar al IDE que tengo un Teensy 2.0, y cada vez que compile, fijarme en donde ha almacenado el fichero hex con el código objeto (normalmente en /tmp/arduino_build_XXXXXX, y utilizar el comando

sudo ubaboot.py write path_al_fichero.hex

para programarlo.

Problemas tecladiles

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.

Distribución ISO renderizada desde Kicad
Distribución ANSI renderizada desde Kicad

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.

Parche para Gnome Boxes

Aunque hasta ahora utilizaba VirtualBox para hacer virtualización, hace unas semanas me dio por probar Gnome Boxes en su lugar, y la verdad es que me ha convenció mucho. Es cierto que no tiene muchas características avanzadas de VirtualBox, como son el portapapeles bidireccional o el acceso directo a USBs, pero la verdad es que, en general, no los necesito. Y, sin embargo, sí tiene algo que me era especialmente útil, que es que la emulación de la tarjeta gráfica funciona de manera nativa en Fedora y en Debian, sin necesidad de instalar nada. El motivo de que esto me resulte especialmente útil es que tengo algunos parches pendientes para mutter, el gestor de ventanas de Gnome, y la única manera de probarlos en condiciones (sobre la versión de desarrollo) es trabajar con Fedora. Por desgracia, no se qué pasa que los drivers de VirtualBox para la pantalla no me funcionan en Fedora. No es que funcionen mal, es que no he conseguido que me funcionen. En alguna ocasión parece que van, pero de pronto dejan de funcionar sin motivo aparente. Y depurar en un sistema en 640×480 es imposible. Por eso estoy utilizando Gnome Boxes para todo esto, porque me funciona perfectamente.

Por desgracia, hace unos días me encontré con que era imposible crear una nueva máquina virtual en Boxes: cada vez que escogía la opción en le menú, el programa se cerraba con un core dump.

Tras indagar un poco descubrí que el problema era que el programa se conectaba a una URL externa para bajar una lista de recomendaciones y mostrar las tres más interesantes; sin embargo, por algún motivo, todas menos dos fallaban a la hora de descargarlas. Y aquí llega el problema: el código no comprobaba cuantas recomendaciones había e intentaba mostrar siempre tres, pero al haber sólo dos, cascaba. Como solucionarlo era trivial, decidí escribir yo mismo el parche y enviárselo, y lo aceptaron de inmediato, por lo que ya vuelve a funcionar perfectamente.

Y aún me preguntan por qué me gusta tanto el software libre…

Galletitas

Parece que ha empezado la trasposición de la ley europea de cookies, y ahora es obligatorio que cualquier página web que haga uso de cookies lo avise claramente. Por eso a partir de ahora aparecerá un pequeño aviso en la parte inferior de mi página (aunque una vez eliminado, no volverá a aparecer).

Desde luego quiero dejar claro que YO (el autor del blog) no almaceno ni utilizo ninguna información personal de los usuarios que visitan esta página. No tengo Google Analytics ni ninguna otra herramienta de seguimiento ni publicidad, y hasta donde yo se, no se debería estar almacenando ningún tipo de información privada sin consentimiento expreso del usuario.

Actualización: tras hacer algunas pruebas, veo que el mero hecho de tener un vídeo de Youtube insertado hace que aparezcan sendas cookies, una de Youtube, y otra de Google. Por otro lado, el pulsar en los botones Me gusta de Facebook, o +1 de Google+ hace que se inserten cookies de estos dos sitios, como cabe suponer.

Actualización 2: la mera visita de mi blog hace que se almacene una simple cookie, que indica que ya se visitó esta página en alguna ocasión. Esta cookie permite, por ejemplo, evitar mostrar constantemente el aviso de que se almacenan cookies y demás.

Usuarios perdidos

Acabo de descubrir que durante la migración del servidor se han perdido los usuarios registrados, así que tendréis que volver a daros de alta para poder hacer comentarios (exigir registro es la única manera de reducir el spam a niveles manejables).

Actualización: la opción de permitir darse de alta estaba desactivada, por lo que era imposible dejar comentarios. Acabo de corregirlo.