Buenas a todos, este mensaje igual es largo, intentaré no ser pesado y alargarme más de la cuenta, pero creo que es importante si queremos exprimir la pantalla y llevarla más allá de sus límites comerciales 😉🙌 entender cómo funciona (además de que hablaremos de conceptos que se pueden llevar a otros dispositivos y sistemas gráficos). La verdad que el hilo me motiva mucho si salen cosas interesantes se merecerá un buen documento técnico.
A ver, voy a intentar explicar por qué te he comentado que aunque las pruebas que has hecho tienen una buena intención, no valen realmente para medir los fps de la pantalla.
Primero hay que entender como funciona la OLED. El objetivo de esta pantalla para sus fabricantes posiblemente no buscan que te vayas a ver una película o te montes algo muy complejo visualmente, al final son pantallas muy baratas y de una gran simplicidad de manejo que buscan que al usuario le sea fácil crear pantallas visuales, informativas....
Sea por interfaz SPI o I2Cl, la pantalla da sensación al usuario de ser persistente, es decir, si lanzas una imagne, una fuente, una línea, esta se queda ahí fija mientras no sobreescribas esos pixels. El usuario desde donde la programe, sea una FPGA o un arduino, no tiene que preocuparse de repintar la pantalla continuamente, simplemente manda los pixels a pintar en un momento dado y no tienes que preocuparte más.
En otros displays, como una pantalla VGA la complejidad es mayor porque no existen comandos como tal para pintar o limpiar la pantalla sino que hay que hacer todo el trabajo a mano. En qué consiste el flujo en una VGA? vamos a verlo porque es clave entenderlo para entender bien la OLED.
En la VGA hay un sistema electrónico que recorre cada pixel de la pantalla por lineas , de arriba a abajo, es decir empieza por la esquina superior izquierda, avanza hasta el final, vuelve al inicio pero bajando una línea de pixels, avanza hasta el final, vuelve al inicio bajando otra línea ..... y así hasta completatr todos los pixels de la resolución en la que estemos trabajando.
Esto recrea el tubo de rayos de los monitores antiguos, los monitores tenían un cañón que recreaba este movimiento en zig-zag, línea a línea recorriendo la pantalla. Hoy en día no hay cañón de rayos pero las pantallas recrean este funcionamiento.
Para que podamos pintar los pixels en su sitio correspondiente y a tiempo , la pantalla nos da una serie de señales de sincronización. Nos manda una señal cuando termina de pintar una línea (refresco horizontal) y una señal cuando termina de pintar toda la pantalla (refresco vertical) (el pintado va de forma autónoma, la pantalla no está esperando a que le digamos ¡pinta!, sino que ella va pintando y somos nosotros los que tenemos que darle los datos en el momento preciso).
Con estas dos señales ya sabemos cuándo tenemos que empezar a pintar (por ejemplo después de un refresco vertical), o cuando tenemos que cambiar de línea.
Pero nos falta algo importante que es, cómo sabemos cuando hay que cambiar de pixel, para poder ir dándole pixel a pixel, justo cuando lo necesite. Esto se obtiene con el reloj de pixel o como se quiera llamar que va relacionado con el refresco de la pantalla.
Cuando leemos por ejemplo "resolución VGA 640x480 a 60Hz", esto quiere decir que vamos a pintar visualmente 640x480 pixels 60 veces por segundo, es decir vamos a pintar 307200 pixels cada 1/60 segundos (a esto en vga hay que sumarle áreas de pixels que no se ven en las áreas exteriores de la pantalla pero ahora por simplicidad las vamos a obviar, lo importante es el concepto.
Eso quiere decir en un segundo quiero pintar: 307200 píxeles/cuadro×60 cuadros/segundo=18432000 píxeles/segundo.
Eso quiere decir que simplificando si queremos pintar cada pixel en un ciclo de reloj necesitaremos una frecuencia de 18.432 Mhz
Entonces recapitulando, tenemos nuestro sistema ficticio a una resolución de 640x480 pixels a 18.432Mhz lo que no permite pintar la pantalla 60 veces por segundo lo que hace que el movimiento sea super natural, no haya flicks de pantalla,etc (si habéis visto alguno de los últimos hilos de VGA veréis que el reloj es mayor, esto es por lo que os comentaba que en un sistema VGA hay que contar con pixels no visibles, pero insisto en que esto es una simplificación para entender el concepto).
Seguimos con los conceptos ya sabemos lo que implica pintar los pixels a esta resolución, entonces ahora viene un típico lío mental ¿qué son los FPS? si os fijais muchas veces se mezclan los dos conceptos (en videojuegos, etiquetas en monitores....)....
Por un lado está el sistema de la pantalla, que SIEMPRE, va a pintar 60 pantallas por segundo, cambie o no cambie la información. Teneis que imaginaros como si fuera una máquina en bucle que va cogiendo de la memoria cada pixel y lo pone en pantalla, uno tras otro sin parar y cuando acaba vuelve a empezar y así infinitamente.
Esto como quien dice es incontrolable por nuestra parte, la pantalla no va a parar de pintar jamas ni la podemos frenar ni acelerar.
Ahora bien, entra en juego la aplicación del usuario, aquí el ejemplo de Demócrito viene perfecto, cuando dice que dentro del módulo está el algoritmo de bresemhan y tarda un rato en procesarlo.....
Se abre un nuevo "espacio-tiempo", es como si se desdoblara la realidad y tuviéramos dos universos paralelos a diferente tiempo, uno, en el que vive la pantalla qeu va como una moto y nada le frena y pinta y pinta sin parar. Y por otro está el universo de la aplicación del usuario que tiene que hacer cosas antes de "mandar a pintar".
Vamos a ver el sistema completo con un único reloj, el de los 18.432Mhz. Como la pantalla y el aplicativo se rigen por la misma velocidad quiere decir que para pintar a 60 FPS, tendría que poder decidir que pintar en cada pixel en 1 ciclo de reloj, lomismo que la pantalla pinta. Aquí algo ya chirría... necesitaríamos empezar 1 ciclo antes que la pantalla, imaginaos coloco en la primera dirección de memoria el primer pixel, y cuando detecto la señal de sincronización vertical (se ha acabado de pintar un cuadro y empezams de nuevo a pintar), entonces escribo el pixel 2 de forma que cuando la aplicación escribe el pixel 2 la pantalla está pintando el 1, cuando yo escribo en memoria el pixel 3 la pantalla pinta el pixel 2 (la pantalla nos persigue). De esta formasi en un caso ideal en el que por ejeplo tengo los frames de una película almacenados en una memoria en la que puedo acceder frame a frame y pixel a pixel en 1 ciclo de reloj, podría pintar a 60 FPS, es decir FPS a mi me gusta verlo como los frames por segundo del usuario.
Como veis esto sería un caso ultra ideal, pero en la realidad es algo imposible. Al mismo ciclo de reloj por ejemplo, sólo en acceder a memoria se nos iría más de un ciclo y si hay que hacer algún cálculo , por ejemplo para la línea con el algoritmo de bresenham se nos irá más de uno y dos ciclos por cada pixel.
De esta forma la aplicaciónd el ussuario va a tener menos FPS que Hz de la resolución, es decir si por ejemplo tardamos el doble de tiempo en preparar una pantalla que en pintarla pues nos dará tiempo a pintar a 30FPS. Esto se ve claramente cuando en un equipo antiguo se pone un videojuego moderno, de repente va todo a "tirones" por qué pues porque nuestro equipo no es capaz de hacer los cálculos necesarios para pintar a 24-30FPS mínimo que da una fluidez natural, en cuanto baje a 15-12FPS el usuairo notará que va a trompicones y ya si el cálculo es muy pesado y bajamos a 10-5-1FPS pues literlamente no se podrá jugar. En todos esos casos , desde el de 50, 30, 15-1FPS la pantalla sigue pintando a 60Hz, es decir 60 pantallas por segundo, lo único que pinta varias veces la misma pantalla.
Otra opción que por ejemplo ocurre en equipos modernos o contarjetas gráficas más modernas es que el reloj del sistema y el de la pantalla son diferentes. La pantalla sigue llendo a sus 18.432Mhz pero elsistema va mucho más rápido por lo que en un momento dado podría pintar los 60FPS que se necesitan para pintar los 60Hz (imaginemos por ejemplo qu eel sistema va a cientos de Megaherzios o a gigaherzios).
Como veis es un tema de sincronización, a groso modo eso es en lo que se basa todo el tema de display gráfico.
En el ejemplo estamos suponiendo que la misma memoria destinada a almacenar lo que se ve en pantalla es la que se recorre para plasmarse físicamente en la pantalla que vemos. Como podemos intuir se pueden dar varios problemas. Los más comunes:
- Tearing, puede ser por defecto o por exceso. Imaginemos que la aplicación rellena más rápido la memoria que la pantalla pinta. En este caso lo que ocurre es que la pantalla va pintando y por ejemplo si la aplicación rellena la memoria al doble de velocidad que la pantalla pinta, se da el caso que a mitad de pantalla (hemos pintado ya la mitad superior de el frame 1), la aplicación ya ha rellenado la memoria con el frame 2, de este modo la pantalla a a pintar la mitad inferior con el frame 2, es decir tendremos simultáneamente la mitad del frame 1 y la mitad del frame2 y a´si sucesivamente.
Y puede pasar al revésque rellenemos más lentos de lo que la pantalla pinta y la pantalla nos coma.
en cualquiera de los dos casos lo que ocurre es que empiezan a generar artefactos visuales inconsistentes , si hay movimiento pueden darse superposiciones, flicks o sensaciones visuales raras.
En este vídeo se ve muy bien el ejemplo:
Como en el scroll se come el frame a mitad o antes de acabar el anterior las barras se sienten "partidas".
- Aliassing, este efecto es como lo que decía Demócrito de la bicicleta, aquí lo que ocurre es que rellenamos la memoria mucho más rápido de lo que la pantalla pinta, entonces qué ocurre que por ejemplo empezamos a pisar frames y si por ejemplo tenemos imaginemos un video de una rueda de bicicleta dando vueltas, en vez de pintar los frames 1,2,3,4....al pisarlos porque pintamos muy rápido, lo que se pinta en pantalla es el 1,3,6,8,11.... es decir se saltan frames lo que visualmente genera el efecto de la rueda que parece que empieza a ir para atrás (Esto ocurre porque nuestro cerebro no puede procesar todos los "frames" de la rueda girando a mucha velocidad y al saltárselos se genera ese efecto visual de cambio de dirección) O por ejemplo habréis visto alguna vez el típico vídeo en el que el agua slaiendo de un grifo parece estar congelada, esto es porque se sincroniza el framerate dela grabación de la cámara con el de la velocidad del agua y al saltarse frames de forma sincronizada, el agua parece no moverse en el vídeo y así mile de ejemplos (helicópteros volando con las aspas paradas.....) Aquí tenéis unejemplo de undersampling
https://www.youtube.com/watch?v=ByTsISFXUoY , si no queréis ver todo el vídeo lo podéis ver en el minuto y treinta y siete segundos aproximadamente donde la hélice parece estar parada.
Todo estos son problemas de sampleado que se da en los ADC por ejemplo (todo converge siempre a niquist XD ).
Para evitar esto habréis oído hablar del "doble buffer" en temas de vídeo, esto básicamente es tener dos memorias, una en la que la aplicación va creando el siguiente frame y otra en la que está el frame que se está pintando. De este modo se solucionan muchos problemas porque el aplicativo puede tardar lo que necesite en terminar el siguiente frame en la memoria que no se ve, y cuando acaba, lanza una copia de esa memoria a la de pintado (sincronizado con el refresco vertical para que se empiece el nuevo frame desde la esquina superior izquierda ).
Como podéis intuir esto hace que todo vaya más suave y sincronizado, aquí solo aparece el problema de bajos FPS, es decir que tarde mucho en pintar y aunque vaya sincronizado te tengas que saltar frames naturales. El aliassing desaparece porque el ritmo va marcado por las sincronizaciones verticales y eso "frena" al sistema si tiene capacidad de ir muy rápido, así nunca se pisan frames, es decir con doble buffer en principio sólo tenemos el problema de no dar de si y que la aplicación se sienta lenta.
En cualquier caso sigo insistiendo en tener claro que la pantalla se pinta a 60Hz, el buffer de pintado no frena , va como un reloj suizo.
Teniendo esto claro , espero haberme explicado bien, vamos a retomar el caso de la oled de 128x64
en este caso la oled "oculta" al usuario los problemas de sincronización. La pantalla lleva un pequeño micro que gestiona todas las señales de pintado y los sincronismos y lo que expone al usuario es una interfaz i2c o spi de forma que el usuario sólo se tiene que preocupar de enviar pixels y direcciones de memoria de dónde pintar.
Como he dicho anteriormente para aplicaciones sencillas, que no necesiten FPSs altos (displays con números , mensajes de texto, etc) es más que suficiente, si enlazáis con lo anterior, no hay ningún tipo de sincronización entre pantalla y datos.
Internamente el micro de la OLED tiene su propio oscilador que define la frecuencia de pintado (nuestros famosos 60Hz anteriores), la fórmula es la que os comenté en un post anterior y que viene en la documentación, echando cuentas no debe tener un refresco mayor a 15-20Hz es decir la pantalla OLED en el mejor de los casos va a pintar 20 imágenes por segundo.
Este oscilador es el que podemos tocar con el registro D5, lo que pasa que está bastante mal documentado y por lo que he podido investigar cada pantalla de cada fabricante puede tener un oscilador a diferente frecuencia (unos puede ir a 300Khz otros a 400Khz..pero todos entorno a estos valores).
Este micro, hace la labor del sistema de pantalal denuestro ejemplo. La pantalla OLED tiene su propia memoria (una SDRAM) que es la que se rellena con los datos ue mandamos por i2C o SPI.
El micro una vez inicializado, hace la labor de nuestro sistema de pantalla del ejemplo, PINTA SIN PARAR, es decir empieza por la dirección de memoria 0 y se la recorre entera desde la esquina superior izquierda hasta la esquina inferior derecha (independientemente de modo de pintado seleccionado en el I2C).
Es decir la pantalla nos genera una ilusión de pantalla persistente, pero físicamente no lo es (no es como una pantalla de tinta electrónica en la que físicamente se reconfiguran las propiedades de los pixels y cambian de estado). Para dar la sensación de que la información no se va aunque desde el arduino o la FPGA no mandemos información , simula esa persistencia con el micro interno, que pinta y pinta sin parar.
Todo el tema de paginación etc es una interfaz de cara a los usuarios de la pantalla (nosotros) internamente como os digo la pantalla funciona de forma similar al eejmplo que os he puesto.
De este modo aquí existen dos universos paralelos. Por un lado está el universo del micro que vive en la pcb de la pantalla que pinta a a10-15-20Hz en función de lo configurado en el registro D5 y la marca de la pantalla. y por otro lado está el universo de nuestra aplicación en la FPGA, en el Arduino.... Como son dos universos paralelos, Demócrito que habita en el universo SPI que va a toda velocidad, se lia a mandar frames a diestro y siniestro pero en el universo de la pantalla no va a pintar más rápido. Lo que ocurre es que Demócrito está pisándose sus propios frames, cuanto más rápido pinte más frames se va a saltar porque la pantalla irá a su ritmo y nosotros no haremos más pisar la memoria.
Por este mismo motivo aparecen artefactos raros , flicks, etc (no vamos sincronizados).
Gente que está usando esta pantalla para pequeños ordenadores educativos y cosas similares se enfrentan a este problema porque cuando quieres hacer animaciones en estas pantallas la sincronización no es posible de forma natural.
Viendo la documentación, resulta que hay un pin que marca la sincronización de la pantalla (como lo hace el sincronismo vertical en la VGA) pero resulta que no está expuesto en el conector soldado de la parte trasera, está junto al microtras la pantalla o debajo de ella (el micro muchas veces está debjo de la pantalla en la propia tira flexible bajo un pegote negro.
Esta semana no tengo equipamiento a mano pero si os interesa ( a mi me ha picado) en cuanto tenga a mano mi equipo igual disecciono la pantalla que tengo rota e intentamos pinchar el pin de sincronización para ver el refresco real y su relación con el parámetro D5 y así saber claramente a qué frecuencia vamos.
Otra forma de probarlo así "cutre" sería tomar dos imágenes muy distintas para notar el cambio e ir probando diferentes frecuencias y cambiar cada segundo una por otra (ir probando a 5fps,10fps,etc...) hasta ver en cual el cambio es natural y no se parte hace algun raro. Lo que pasa que surge el problema de saber cuando empieza cada frame. Es complicado la verdad he estado mirando bastante y no hay soluciones buenas si se quiere hacer cosas chulas con esta pantalla.
Le ando dando vueltas a esto porque tengo varias ideas chulas que quiero probar y dependen de poder sincronizarnos (la verdad que daba por hecho que existiría uncomando o algo que nos permitiera saber cuando se hace el sync del frame pero me he llevado una buena sorpresa).
Retomando lo anterior y mi mensaje antes de las pruebas de Demócrito en estas pantallas tanto en SPI como en I2C da igual porque en ambos los FPS máximos son perfectamente asumidos por la velocidad de los dos buses (siempre que quieras ir sincronizado más o menos y no comerte frames).
La diferencia y por eso en muchos vídeos tiran del spi a tope es porque en micros tipo arduinos en los que la programación es básicamente secuencial o pseudoparalela, la aplicación espera a pintar para seguir procesando, entonces en ese caso el spi es mucho mejor, porque al transmitir más rápido nos libera para poder seguir procesando y en muchos casos puede hacer que en spi la aplicación vaya super fluida (orque transmite en menos de 1ms un frame y ya te puedes poner a calcular el segundo y en i2c tarda 23ms por frame por lo que has perdido todo ese tiempo para calcular cosas lo quepuede implicar que tardes más en calcular y al final pierdas frames efectivos.
En la FPGA cambia bastante el panorama, porque podemos paralelizar el proceso de la información y el envío y crear un pipeline no bloqueante (un ejemplo estupendo de la ventaja de una FPGA frente a un arduino por ejemplo en una aplicación de este tipo que es una cosa mítica que la gente suele preguntar muchas veces,....esto también lo hago con un Arduino...). En este caso como el sistema que traslada a la pantalla va paralelo a generar el frame, la velocidad de transmisión no es cuello de botella para los Hz máximos de la pantalla.
En conclusion, las pruebas ue ha hecho demócrito prueban el ancho de banda del bus gráfico pero no del refresco de la pantalla (es decir has medido lo rápido que puedes mandar frames en un sistema y en otro, aunque en cualquiera de los casos se pinte al mismo FPS).
Me ha quedado más largo que la biblia en verso, espero que al menos al que le interese le haya sido ilustrativo y ayude a aclarar conceptos super importantes tanto para la OLED como para cualquier otro display gráfico.
En la OLED quiero hacer algún ejemplo animado sencillo para familiarizarme con el módulo de Demócrito y ando leyendo documentación a ver si se me ocurre como sincronizarnos verticalmente, que tengo una idea peregrina que creo que puede funcionar pero tengo que experimentar un poco antes de contaros. Una pena que no sea fácil de pinchar el pin de sincronización, nos haría la vida mucho más fácil.
Vamos avanzando iré compartiendo avances en cuanto los vaya teniendo, espero que aunque largo os haya servido de algo.
Buenas noches!