Driver para joystick multiplexado para Raspberry PI: hasta 160 switches con 26 GPIOs

El pasado mes de Noviembre le regalé a mi hijo una consola arcade (concretamente una de estas http://arcademadrid.com/50-mando-consola-arcade-hdmi-tv-arcade-2-jugadores). Funciona mediante una Raspberry PI 2, el mueble esta bastante bien y tiene 2 joysticks y 16 pulsadores de buena calidad.
Trasteando con ella he observado que consume casi todos los GPIOs de la raspberry pi 2 para los switches de los pulsadores. Son 24 switches, conectados cada uno a un GPIO, y la raspberry PI 2 tiene 26 GPIOs.
Como me interesa añadir mandos de SNES y un volante de PS2 usando el driver «gamecon_gpio_rpi» https://github.com/RetroPie/RetroPie-Setup/wiki/GPIO-Modules he diseñado una esquema de conexionado nuevo, multiplexando la lectura de los switches, de forma que puedo conectar los 24 switches usando solo 10 GPIOs. Así puedo conectar los mandos SNES y PS2 en los pines que quedan libres
Usando este conexionado se pueden conectar los 24 switches incluso a una raspberry PI 1 que solo tiene 17 GPIOs disponibles en el conector de 26 pines.
Claro que para que esto funcione bien es necesario escribir un driver del kernel de linux que se encargue de leer el hardware y lo muestre en un dispositivo (en el arbol /dev) de forma que los programas lo puedan utilizar.

Una complicación autoimpuesta es que se pueda usar tanto para raspberry pi 1 con conector de 26 pines como con raspberry pi B+ ó 2 con conector de 40 pines intentando dejar siempre como última elección los pines de funciones especiales.
Gracias a esto es posible conectar 2 joysticks con 8 pulsadores (mas 4 de dirección) cada uno dejando libres los pines de TX y RX de UART0 y SDA1 SCL1 del I2C. En la versión 0.0.2 del driver intento también dejar libres los pines que necesita el driver «gamecon_gpio_rpi».

El conexionado eléctrico es fácil, solo se necesita un diodo de señal (1N4148, por ejemplo) por cada pulsador para evitar interferencias al actuar varios pulsadores a la vez. El driver permite 18 tipos distintos de esquemas de conexionado dependiendo del número de switcheslos y joysticks que se deseen usar y del número de GPIOs disponibles.
Yo usaré el «type=14» que permite hasta 5 joysticks de 2 ejes y 8 botones auque el mueble solo necesita 2 de ellos.
El conexionado que he usado es este:
Esquema type=14

En el esquema solo se nombreb los switches con «swX» donde X es un número. Dependiendo del parámetro del driver «map=n» se mapean a los distintos pulsadores de los joysticks siguiendo este mapa:
Mapa SWx

El conexionado multiplexado se basa en cablear los switches en forma de filas y columnas. Las filas se conectan a SALIDAS GPIO de la raspberry y las columnas a ENTRADAS GPIO con el pullup activo. De esta forma con un único grupo de entradas GPIO se pueden leer, de forma alternativa, cada una de las filas. Para leer una fila es necesario poner a nivel BAJO el GPIO correspondiente. Entonces existe un nivel bajo en la fila que los pulsadores pueden enviar a las entradas (columnas) cuando de actuan. El resto de filas se mantienen a nivel alto que es bloqueado por los diodos y es como si no estuvieran conectadas.
Para usar el menor número de GPIOs he subdividido cada fila en dos, de forma que hay dos GPIO de salida por cada fila pero solo la mitad de GPIOs de entrada (columnas).
Con 27 GPIOS disponibles en los conectores de 40 pines de las raspberry se pueden conectar hasta 160 switches (usando el conexionado «type=11») en una matriz de 8 x 20 (en realidad 16 x 10 por que he partido cada fila en dos, asi solo se usan 10 entradas en lugar de 20, pero se usan 16 + 10 pines de GPIO en lugar de 8 + 20).

El driver se instala desde un paquete .deb que he preparado. Se compila en la propia raspberry porque depende del hardware.
Antes de instalarlo es recomendable actualizar la raspberry con:
sudo apt-get update
sudo apt-get dist-upgrade -y
sudo reboot

Luego pueden eliminarse los paquetes fuente de la actualización para ahorrar espacio con:
sudo apt-get clean

Descargar el instalador, worpress no me permite subir .sh «por seguridad» y lo he ‘targzipeado’:
wget https://heli.xbot.es/wp-content/uploads/2016/05/install.tar.gz
tar -xzvf install.tar.gz
sudo ./install.sh

Primero descargará varios paquetes necesarios para la compilación: dkms, cpp-4.7, gcc-4.7 y joystick
Luego descargará los headers del kernel adecuado a la versión instalada, necesarios para la compilación del driver, con un «wget http://www.niksula.hut.fi/~mhiienka/Rpi/linux-headers-rpi/linux-headers-`uname -r`_`uname -r`-2_armhf.deb» y lo instalará con «sudo dpkg -i linux-headers-`uname -r`_`uname -r`-2_armhf.deb»
A continuación conectará con esta página, hará un wget del .deb heli-mpx-joystick-rpi-0.0.2.tar.gz y lo instalará con «sudo dpkg -i heli-mpx-joystick-rpi-0.0.2.deb». Por último eliminará el paquete fuente de los headers, que ya no es necesario, pero los headers se quedan instalados la raspberry en /usr/src/linux-headers-x.x.x.

Todo esto llevará bastante tiempo y es necesario tener una conexión a internet operativa en la raspberry.

En una de mis raspberries he observado un problema en la compilación, debido a la actualización de los headers a a la versión 4.4.9+
"No rule to make target 'kernel/time/timeconst.bc'"

Lo he solucionado copiando el fichero timeconst.bc de http://mirrors.neusoft.edu.cn/rpi-kernel/kernel/time/timeconst.bc a ‘kernel/time/timeconst.bc’

Una vez instalado puede cargarse con
sudo modprobe heli_mpx_joystick_rpi map=2 devices=2 type=14
y probarse con
jstest /dev/input/js0
jstest /dev/input/js1

Jstest tiene un fallo, conocido pero no parcheado (al menos en la versión del paquete ‘joystick’ del repositorio de la raspberry). No muestra correctamente los nombres de los botones del joystick. Es debido a un array de nombres donde faltan tres elementos y eso hace que queden descolocados los demás.
Mientras preparaba esta entrada veo que han actualizado linuxconsoletools, que es el paquete de fuentes donde viene jstest, a la versión 1.5.1 (la anterior era la 1.4.9). Supongo que habrán incluido el parche.
https://sourceforge.net/projects/linuxconsole/files/

El driver puede descargarse del kernel con
sudo rmmod heli_mpx_joystick_rpi

También puede desinstalarse totalmente con
sudo apt-get remove heli-mpx-joystick-rpi-dkms

Fuentes y el resto de los los ficheros del proyecto aqui: https://heli.xbot.es/wp-content/uploads/2016/05/heli_mpx_joystick_rpi_0.0.2_src.tar.gz

Documentos y esquemas del proyecto aqui: https://heli.xbot.es/wp-content/uploads/2016/05/Docs-0.0.2.rar

Para que cargue de forma automática al arrancar la raspberry es necesario añadir en ‘/etc/modules’ una línea:
heli-mpx-joystick-rpi
y crear un fichero de configuración ‘/etc/modprobe.d/heli-mpx-joystick-rpi.conf’ con los parámetros deseados:
options heli-mpx-joystick-rpi map=2 devices=2 type=14

Una vez recableados los switches del mueble original e instalado este driver quedan 16 pines libres donde se pueden cablear los los conectores de la SNES y de la PS2 extraidos de consolas averiadas que he montado en el mueble asi:
Conectores SNES y PS2

Luego uso el driver «gamecon_gpio_rpi» según se explica en https://github.com/RetroPie/RetroPie-Setup/wiki/GPIO-Modules.

Y todo ha quedado asi:
Consola Retropie con SNES y PS2