jueves, 17 de diciembre de 2015

Uso de Librería u8glib para Raspberry Pi con OLED SSD1306

Como parece que me gustan los retos (¿para qué hacerlo fácil, si puedo investigar un poco?), investigué la forma de utilizar la biblioteca u8glib en el Raspberry Pi, para utilizar el OLED que compré en dx.com:
Esta biblioteca permite utilizar directamente una gran variedad de pantallas LCD monocromáticas sin usar X u otras bibliotecas como SDL, pudiendo usar fonts distintos y dibujar líneas, rectángulos, círculos, etc. Tiene un repositorio en Github para el código fuente y manejo de versiones y wiki para la documentación.
Logo U8glib
Aunque en sus orígenes estaba orientada a Arduino, requiriendo poca memoria en esos sistemas, actualmente funciona también en otras plataformas como AVR y ARM.
Esto último es interesante, pues los Raspberry Pi tienen CPU ARM aunque funcionando sobre Linux. Supuestamente no está soportando oficialmente el Raspberry Pi (esto está marcado como mejora en futuras versiones), pero no hay una incompatibilidad en realidad. Al investigar encontré que se le han estado haciendo cambios para soportar al Raspi, ¡y sí funciona!

El módulo OLED en cuestión tiene una resolución de 128 x 64 pixeles, y aunque supuestamente es monocromático, en realidad es bicolor: 16 pixeles (1/4 de pantalla) son amarillos y el resto de color celeste. Aquí va una foto para ilustrar esto:
El módulo permite conexiones SPI (4 y 3 pines) e I2C, cambiando soldando o desoldando pines en su placa, lo que aunque no es difícil, no es como llegar y cambiar.

La biblioteca tiene bastante soporte para este módulo SSD1306, permitendo variedades de OLED con resoluciones 128x64 (como la mía), 128x32 y 64x48, conectándose por SPI e I2C, tanto emuladas por software como usando el soporte de hardware nativo del Raspi. Aunque en la wiki no está muy claro, también permite el uso de doble buffer para ambos tipos de conexiones, lo que en teoría permitiría mostrar animaciones en forma fluida (sin "tearing effect").
Hasta ahora lo he probado solo con SPI por hardware con buffer simple usando 5 pines de datos (más 2 de energía), y no he visto que consuma mucha CPU, que era lo que quería.

Conexiones al Módulo SSD1306

Este es el esquema de conecciones que he usado, mostrando las distintas notaciones para los pines que utiliza la biblioteca.

Pin Módulo OLED Función RPi / Número notación BCM Pin en conector RPi Pin notación WiringPi Función en biblioteca u8gli (Arduino)
1-VCC
+3.3V
pin 17
-
-
2-GND
GND
pin 20
-
-
3-NC
no conectado
-
-
-
4-DIN (data input)
MOSI / GPIO10
pin 19
12
MOSI
5-CLK (clock)
SCLK / GPIO11
pin 23
14
SCK
6-CS (select)
CE0# / GPIO8
pin 24
10
CS
7-D/C (data/command)
GPIO24
pin 18
5
A0
8-RES (reset)
GPIO25
pin 22
6
RESET

La biblioteca 8glib en su sabor Arduino, que es la que probé en su versión del 2/nov/2015, obviamente utiliza notación Arduino. Las personas que están haciendo la implementación para el Raspi utilizaron la biblioteca WiringPi, que tiene orientación similar, pero hay que hacer traducciones entre las distintas notaciones. No me gustó mucho la complicación, pero se puede vivir con eso, especialmente mirando las notaciones de WiringPi y las de Raspberry Pi.

Compilación de biblioteca u8glib en Raspi

Conseguí compilar la biblioteca en el Raspi, usando el siquiente procedimiento:
  • Instalar prerequisitos de compilación en Raspi:
    Me costó al principio la compilación pues yo no detectaba que me faltaban tener instalados los paquetes autoconf y libtool.
    $ sudo apt-get install build-essential autoconf libtool libsdl1.2-dev
  • Clonar el código fuente desde el repositorio:
    En este caso estoy usando la más reciente desde la versión 1.18.1.
    $ sudo git clone https://github.com/olikraus/u8glib.git
    $ cd u8glib/
  • Modificar los programas de ejemplo para que compilen considerando mis conecciones:
    Cambié los siguientes archivos:
    $ sudo nano sys/arduino/Chess/Chess.cpp
    $ sudo nano sys/arduino/U8gLogo/U8gLogo.cpp
    $ sudo nano sys/arduino/GraphicsTest/GraphicsTest.cpp
    buscando la línea que contenía la inicialización del SSD1306 que usara el wardware de SPI :
    //U8GLIB_SSD1306_128X64 u8g(10, 9); 
    // HW SPI Com: CS = 10, A0 = 9 (Hardware Pins are  SCK = 13 and MOSI = 11)
    para cambiarla a utilizar mis conexiones:
    U8GLIB_SSD1306_128X64 u8g(10, 5, 6); 
    // HW SPI Com: CS = 10 @wiringpi (8 @BCM_GPIO), A0 = 5 @wiringpi (24 @BCM_GPIO), RESET = 6 @wiringpi (25 @BCM_GPIO) (Hardware Pins are SCK = 14 @wiringpi (11 @BCM_GPIO) and MOSI = 12 @wiringpi (10 @BCM_GPIO))
  • Configurar la biblioteca y compilar:
    $ sudo ./autogen.sh
    $ sudo ./configure
    $ sudo make
    $ sudo make install
  •  Ejecutar el programa de ejemplo:
    $ sudo ./u8gwpi_logo


Cosas por explorar

  • Cambiar la velocidad de conexión SPI, pues como viene el código fuente invocando a la librería WiringPi, la velocidad es de 100kHz. Esto se cambia en el archivo csrc/u8g_com_raspberrypi_hw_spi.c, dentro de la función u8g_com_raspberrypi_hw_spi_fn, en la línea 77:
    case U8G_COM_MSG_INIT:
    ...
      if (wiringPiSPISetup (0, 100000) < 0)
      {
          printf ("Unable to open SPI device 0: %s\n", strerror (errno)) ;
          exit (1) ;
      }

    Según lo mostrado en la documentación del RPI, en la tabla de velocidades, existen velocidades más altas a las que debería funcionar como 3.2MHz o 7.8MHz. Según la documentación del SSD1306, el límite de velocidad sería 10MHz (parametro mínimo de tSCLK de 100ns. de la tabla 13-4 de características de conexión SPI-4 de esa documentación).
    Sin embargo, en los últimos kernels RPI sobre 3.18, ya está implementado un parche que permite funcionar a velocidades intermedias, mientras sean múltiplos de 2 (post de foro RPI "SPI has more speeds"). ¿Cómo andará a 10Mhz?
  • Medir rendimiento y ver si las animaciones corren en forma fluída usando la invocación con doble buffer.