GLCD 128x64 Driver uPython para raspberry pi pico (aporte)

Scooter

Cascarrabias crónico
No he sido capaz de encontrar como emplear estas pantallas con la raspberry pi pico, así que he escrito un "driver" desde cero.
Hay un video en youtube pero no he visto el código (ahora seguro que encontráis 1001 enlaces)

Es una versión preliminar, a penas ha empezado a funcionar, en cuanto lo pula lo publicaré mas completo.
El archivo es .py pero está como .txt porque no me deja subirlos con esa extensión.
 

Adjuntos

  • SPI-LCD12864.txt
    6.1 KB · Visitas: 5
  • photo_2022-12-20_10-48-06.jpg
    photo_2022-12-20_10-48-06.jpg
    108 KB · Visitas: 18
Versión mejorada, ahora es unas 7 veces mas rápido que antes. Pese a ello sigue siendo lento.
Permite visualizar directamente archivos .bmp arreglados a la resolución de 128x64 con 1 pixel de color.
photo_2022-12-22_22-31-00.jpgphoto_2022-12-22_22-36-17.jpg
La PI pico tiene un "sistema de archivos" dentro y puedes guardar allí lo que quieras, así que basta con subir tal cual los .bmp que hagas en el paint o el programa de dibujo que quieras, lo bueno es que está bastante bien documentado el formato. Los archivos ocupan poco mas de 1kB.
 

Adjuntos

  • SPI-LCD12864c.py.zip
    1.7 KB · Visitas: 3
  • FE.bmp.zip
    522 bytes · Visitas: 2
Última edición:
Te recomiendo el uso de OpenCv para hacer cosas de todo tipo con imágenes, tenés librerías pensadas para python y si encima estás sobre un raspberry la instalación es simplemente un comando en consola.
 
La pantalla tiene dos modos.
En el modo texto es como un display 16x4 con el tipo de caracteres que se ve en la primera foto, además se comporta igual (de mal) que los displays de caracteres; empiezas por la línea uno, sigue por la tres, luego por la dos y al final por la cuatro:

Línea 1
Línea 3
Línea 2
Línea 4

Además está el modo gráfico que se comporta de igual modo:

Inicio de pantalla:
byte 0, 1, 2, 3, 4... 15
byte 32, 33... 47
byte 64, ...
...


Media pantalla:
byte 16, 17, 18...31
byte 48, 49 ... 63
...

Se pueden activar las dos a la vez y hace un "or" entre ambas, así que lo que se puede hacer es poner unos cuadritos en el gráfico y mover números e indicadores en modo texto, algo así:
1671746029848.png
Aunque esta imagen que he pillado de internet, está hecha todo en gráfico porque no es la misma fuente. Se podría hacer las líneas en gráfico y que "bailen" los números en modo caracter lo que resulta en muchísimos menos datos que transferir.

Te recomiendo el uso de OpenCv para hacer cosas de todo tipo con imágenes, tenés librerías pensadas para python y si encima estás sobre un raspberry la instalación es simplemente un comando en consola.
Es una pi pico (microcontrolador), no una pi a secas. Miraré a ver si funciona en uPython, de todos modos un .bmp de 1 bit es como leer una matriz. Es lo bueno de haber aprendido a hacer gráficos con pokes en un c64, que después de eso "todo es sencillo". Leer un .bmp de esos es un bucle while.

Edición rápida: Algo podría quedar, lo he hecho rápido:
photo_2022-12-22_23-20-52.jpg

Eso es una mezcla de un .bmp hecho con gimp o con el que se quiera:
1671747760057.png
Mas las letras puestas en la pantalla de caracteres.
Esta pantalla tiene pocos caracteres en ROM; el (º) no lo tiene por eso lo he hecho gráfico.
Las gotas son un poco bodrio; el "pixelart" no es mi fuerte.
Así "es lo mismo pero no es lo mismo " que un 16x4; queda mas bonito.
 
Última edición:
Tratando de optimizar el código para el GLCD 12864 encontré que se puede mezclar sin mas ensamblador con μPython, así sin dramas.
Como lo que quiero acelerar es una cuestión de ordenar bits y generar pulsos me puse manos a la obra.
Resulta que el rp2040 se maneja según su datasheet:
2.3.1.7. List of Registers
The SIO address base is 0x0d0000000
0x010 Latch de salida
0x014 Para borrar pins
0x018 Para activar pins
0x01c Para "voltear" pins; hace un xor

Primero se configura el pin de forma normal:
Código:
led = machine.Pin(17, machine.Pin.OUT)
Nota: Esta configuración se puede hacer cambiando los registros del SIO pero como solo se hace una vez, el beneficio en velocidad es insignificante y a cambio el código se hace ilegible, por eso he preferido dejarlo en python "normal".

Entonces podemos crear una función en ensamblador de la siguiente forma:
Código:
@micropython.asm_thumb
def toggleled():
    mov (r1,0x0d0)
    lsl (r1,r1,24)   #Cargar r1 with 0xd0000000 puede que se pueda hacer mas sencillo
    mov (r0,1)       #Cargar r0 con la máscara de pines que se van a cambiar
    lsl (r0,r0,17)    #Mi led está conectado al pin 17,
    str (r0,[r1,0x1c])   # 0x1c es XOR, (voltear). 0x14 para borrar o 0x18 para activar el pin
Cada vez que se llama a esta función el pin cambia de estado.

La historia es que cada byte que se envía a la pantalla son 24 bits que hay que enviar y 24 pulsos de reloj, en mandar 1kB que es un pantallazo gráfico tarda 400ms en python. Ahora veré cuanto tarda en ensamblador.
 
Tras varias pruebas ha habido éxito:
He reeescrito la función que manda un byte a ensamblador. Benchmarks:

- 278ms "en no hacer nada", osea, hace todo el bucle de leer el bitmap pero sin escribir en la pantalla ni borrarla.
- 298ms con la rutina en ensamblador
- 998ms en hacerlo en python puro
El resultado es que ahora tarda 20ms en mandar dos pantallas una de borrar con todo a 0 y la otra un bitmap mientras que en python puro tarda 720ms en hacer lo mismo. Para enviar 1024 bytes 10ms frente a 360ms
No se puede ir mas rápido porque el display no lo capta, si he tenido que poner NOPs y sleeps de microsegundos en algunos sitios porque si no no va. Probablemente se pueda bajar algún ms pero serán pocos.
 

Adjuntos

  • GLCD12864asm.py.zip
    1.3 KB · Visitas: 1
Es bastante absurda la mezcla que he hecho, o no.
Por un lado python interpretado lentísimo mezclado con ensamblador que lo bloquea a este tipo de CPU, pero bueno, es un "driver" de un hardware específico que lo bloquea igualmente al hardware.

Para que veáis que no es taaaan lento:
El LCD en modo serie requiere tres bytes por cada byte que se envía, el fabricante sabrá por qué, un byte de "comando", luego envía 4 bits en un byte y luego otros cuatro en otro.
Eso implica 24 pulsos de reloj y 24 datos.
Esos pulsos no pueden ir a cualquier velocidad, están limitados a 1MHz o así.
El programa abre un archivo .bmp estandard sin manipular de su sistema de archivos, elimina cabeceras y colas y convierte los datos para que los acepte el lcd. Si esto se hace en un PC y se le pasa un .bin arreglado irá más rápido. Cada línea del LCD son 4 o 5 comandos que dan las coordenadas y 16 bytes del gráfico, ello por 64 líneas y todo por 24 +24 operaciones de bit para manejar los dos pines.

Casi 1 segundo!!! Es un monton...

Por qué es taaan lento ese microcontrolador?
O sea, cuál es el limitante en éste caso, la pantalla o el microcontrolador?
Python es interpretado osea lento*
Probablemente yo no sea el mejor pythoniso del mundo y se pueda mejorar el algoritmo; el primero que hice era siete veces más lento.
Python a secas corriendo en una CPU de varios GHz es rápido de sobra, pero μPython en este caso correr sobre una CPU de 100MHz y para tareas pesadas se resiente.
Por cabezonería no he usado uno de los puertos SPI, que no tengo claro si solo hay uno o hay varios porque la documentación es confusa. Usando el SPI hardware debería de igualarse la velocidad con el código que he hecho.

*Es lento en ejecución, en depuración es mucho más rápido que compilar, tarda 0s en ejecutar probar, volver a cambiar...


Ya es manía de cada uno, a mí me gusta muchísimo más la sintaxis de python que la de todo lo que he probado hasta ahora. Esta placa se puede programar en C pero ni lo he intentado.

La porquería del Phyton, que se ha transformado en algo parecido al VisualBasic para los que no tienen NPI de diseño de software....
Parecido al VB...,"cualquier mermo puede programar en Phyton"...
Me declaro memo entonces.
¿has probado su sistema de tablas, tuplas etc y lo has comparado con cualquier otro lenguaje?

Indudablemente python consume mas recursos, pero es que me cuesta lo mismo una cpu de 10mips que una de 100 y es ideal para memos por no compilar 0s entre prueba y prueba.

Quizás sea deformacion del basic de los 80 en lo que aprendí a programar o será por ser memo, el caso es que me encuentro mas comodo usando python que C o que cualquier otra cosa.

Evidentemente cada uno que programe como quiera.

Voy a investigar un poco más que hace con el ensamblador, si lo compila o lo interpreta porque no tengo claro como un intérprete usa el ensamblador que como su nbre indica se "ensambla"≈"compila".
 
Última edición:
¿has probado su sistema de tablas, tuplas etc y lo has comparado con cualquier otro lenguaje?
Si, y en eso tiene mucha potencia...sobre todo expresiva, pero tambien los he probado en C++, en PHP y en Java...y son iguales.
Indudablemente python consume mas recursos, pero es que me cuesta lo mismo una cpu de 10mips que una de 100 y es ideal para memos por no compilar 0s entre prueba y prueba.
Y eso es parte del problema: ninguna estructura de datos avanzada se usa en aplicaciones embebidas así que la facilidad del Python en esas cosas puede cuestionarse. Por otra parte, los precios de las CPU (microcontrolador) no son los mismos, y si un pi pico te sirve para programar y jugar..está perfecto, pero si estás diseñando un sistema embebido del que vas a producir y vender varios millones de unidades (TV, lavarropas, microondas...por no sofisticarlo mas, como una ECU) dudo mucho que se decida pagar 1 dolar mas por algo con potencia de sobra...que nunca va a usarse, o que de va a usar en facilitarle la vida al desarrollador...
Por eso lo de "cualquier mermo programa en Python"...que además no lo dije por vos, por que sé que estas jugando y experimentando, pero es una muy mala idea usar un controlador embebido programado en Python para diseñar aplicaciones que no sean prender unos leds o medir algo y mover un motor o un relay....y no hablemos de pretender controlar un motor (con un PD o PID dependiendo si es velocidad o posición) en tiempo real usando Python.
Hay otras cosas, como la seguridad del código frente a una gestión de memoria "desconocida", los tiempos de reconocimientos de las interrupciones, etc, etc...pero no dá para tocarlos aquí.

A eso me refería....
 
Estoy de acuerdo con todo lo que has expuesto.
Para algunas cosas sirve y para otras no.
Probaré el C en esta tarjeta a ver qué tal va.
Lo que me ha gustado es ensamblador de ARM que nunca había tocado, me ha recordado mucho en el "espíritu" al 6502.
 
Todo lenguaje de alto nivel ejecutado "al vuelo", es decir, interpretado, siempre va a ir más lento que compilado.
Eso es normal y ha sucedido siempre.

*Es lento en ejecución, en depuración es mucho más rápido que compilar, tarda 0s en ejecutar probar, volver a cambiar...
Eso también es normal, porque no tiene más que leer el código de la memoria y mostrarlo. Y luego escribir unos cambios mínimos.

No se puede ir mas rápido porque el display no lo capta, si he tenido que poner NOPs y sleeps de microsegundos en algunos sitios porque si no no va

Entonces, está claro que el problema no es ni del micro, ni de Python, ni de que éste sea interpretado o compilado.
La culpa es del display, que no admite una velocidad mayor y ello obliga a introducir pausas en el código.

"Un sistema es tan rápido como el más lento de sus componentes"
 
Última edición:
Atrás
Arriba