Las pantallas gráficas, incluidas las del tipo OLED, más representadas en nuestro mercado por Winstar, tienen una demanda mucho menor en relación con las minúsculas y las publicaciones sobre su uso también son mucho menores. Mientras tanto, son las pantallas OLED gráficas las que, debido a la falta de unión a las tablas de fuentes de un patrón predefinido, proporcionan la mejor manera de obtener dispositivos de visualización ergonómicos para una amplia variedad de necesidades. Además, resultó que el modo gráfico en el controlador WS0010 es más fácil de iniciar y funciona más estable que el modo de texto.
Antes de proceder a la consideración de las pantallas gráficas reales, consideraremos el problema perenne con los problemas de encender el modo de texto del controlador WS0010, que recibió una solución inesperada y obvia (¡oh, dónde estaban mis ojos!).
Resolución de problemas con el modo de texto WS0010
Es bien sabido que las pantallas de la línea Winstar tienen problemas de estabilidad durante la inicialización. Por cierto, resultó que esto no era exclusivo de los "malditos chinos": las muestras de Newhaven Display 16x2, que obtuve con gran dificultad, ubicadas en el otro lado del globo, son externamente una copia completa de Winstar, excepto por la ubicación de algunas inscripciones y el nombre de la empresa en la mancha ( la misma forma y fuente):

Al contener, como está escrito en las hojas de datos, un cierto controlador "LCD comparable", estas pantallas se comportan completamente idénticas a las chinas y tienen las mismas desventajas. Obviamente, no debe pasar tiempo revisando otras compañías, como Midas: a juzgar por esta
publicación , no podría prescindir de la cooperación internacional. La economía globalizada gobierna!
Las dificultades del modo de texto se expresan en el hecho de que al iniciar (por ejemplo, al reiniciar o restablecer manualmente el programa del controlador de control), puede aparecer basura en las pantallas, y las líneas 0 y 1 cambian de lugar al azar. Los experimentos mostraron que no depende del método de inclusión (8 bits o 4 bits). Este problema es especialmente grave cuando se requieren reinicios periódicos del software, por ejemplo, por Watchdog-timer.
Parte del problema es una actitud ordenada hacia el poder (desde una fuente separada, y de ninguna manera desde USB Arduino), y un reinicio por separado apagando y encendiendo la pantalla después de iniciar el programa de control (ver la
publicación anterior del autor). Al final resultó que, el autor de estas líneas no fue el único que propuso una solución similar al problema: el autor del complemento
LuquidCrystal llamado
WinstarOLED también incluyó un pw_pin especial en él, con el cual la potencia de la pantalla se distorsiona en el momento en que comienza el programa.
Pero esto es todo, por supuesto, iniciativa y medias medidas. Alguien SeregaB encontró una forma radical (vea
su publicación en easyelectronics.ru, gracias a
Tomasina por la información). De hecho, planteó una tarea completamente diferente: aprender a trabajar solo con modo gráfico en lugar de texto. Intentando cambiar entre modos, descubrió rápidamente que "
cambiar al modo gráfico era normal, y de gráfico a" texto ", muy torpe ". Luego recordó que "
una vez, hace mucho tiempo, cuando las DSh todavía se imprimían en papel, en algunas de las DSh en el HD44780 leí que los modos de cambio solo deberían hacerse cuando la pantalla está apagada ". Y funcionó.
De la publicación citada, simplemente reproduciré dos procedimientos de conmutación aquí, adaptándolos ligeramente para su uso con LuquidCrystal (la instancia de clase se llama OLED1 aquí).
Cambiar al modo gráfico:
OLED1.command(0x08);// OLED1.command(0x1F);// OLED1.command(0x01);// (.. clear()) OLED1.command(0x08|0x04);//
Cambiar al modo de texto:
OLED1.command(0x08);// OLED1.command(0x17);// OLED1.command(0x01);// (.. clear()) OLED1.command(0x04 | 0x08);//
Como veremos más adelante, el primer procedimiento no es realmente necesario: el WS0010 cambia al modo gráfico desde media patada, solo envía el comando 0x1F. Pero la segunda secuencia de comandos fue el caso. Para la muestra, se incluyó directamente en el boceto usando LuquidCrystal de esta forma:
void reset_textmode() // { OLED1.command(0x08);// OLED1.command(0x17);// OLED1.command(0x01);// OLED1.command(0x04 | 0x08);// }
Luego se llamó a esta función en la configuración justo después del inicio de la biblioteca:
. . . . . OLED1.begin(16,2); //16 2 reset_textmode(); // clear() . . . . .
Si inserta un retraso (500) antes de esto, la demostración resulta muy clara: después de presionar el botón de reinicio de la placa Arduino, generalmente aparece basura en la pantalla, pero solo por un momento: después de que se activa la función, la pantalla se borra y todas las líneas vuelven a su lugar .
La función funciona así, pero por conveniencia, reemplacé el contenido de la función LiquidCrystalRus :: clear () en el archivo de la biblioteca mejorada LiquidCrystalRus_OLED.cpp que se discutió anteriormente con esta secuencia de comandos (le recuerdo que
puede descargarlo del sitio del autor). No hay que esperar a que el comando se ejecute en la biblioteca, por lo tanto, para mayor confiabilidad, después de cada comando hay demoras de 100 μs insertadas en el estilo general de la biblioteca. En los bocetos que utilizan esta variante de LiquidCrystalRus_OLED, al comienzo de la configuración, es necesario llamar a la función clear () y, al mismo tiempo, borrará la pantalla.
NotaHay un problema con la limpieza de la pantalla: en la hoja de datos de la tabla de comandos se observa que el comando 0x01 puede durar hasta 6,2 ms "cuando fsp o fosc = 250KHz". Qué tipo de "fsp o fosc" se encuentra realmente en controladores específicos, eran demasiado flojos para escribir, pero en cualquier caso, incluso si es megahercio, el retraso para este comando debería ser significativo (y el autor de LiquidCrystal lo menciona). Sin embargo, en la práctica, resulta que el equipo de limpieza trabaja por sí mismo si no hay demora en absoluto. Así que no lo entendí, pero actué según la conocida regla de programación: "funciona, ¡no lo toques!".
Ahora, finalmente, tratemos con el modo gráfico.
El modo gráfico en pantallas de texto WEH001602
Para empezar, intenté cambiar la pantalla de texto que tenía WEH001602BG a modo gráfico. Tenga en cuenta que las pantallas gráficas de 100x16 y texto (configuración de 20x2, 16x2 solo tienen menos puntos horizontales) tienen matrices idénticas, solo las pantallas de texto están separadas por intervalos en familiaridad. Esto limita severamente el uso del modo gráfico en pantallas de texto, e incluso más modo de texto en gráficos. Pero para probar cómo funciona, puede usar cualquiera de ellos.
La pantalla junto con el DS1307 se conectó al Arduino Nano de acuerdo con el siguiente esquema:

Según el mismo esquema, conectaremos pantallas gráficas en el futuro. El color gris en el diagrama muestra la conexión de la segunda pantalla, si es necesario.
Para cambiar al modo gráfico, puede usar el procedimiento mejorado de la sección anterior, pero una función simple de un solo comando funciona:
. . . . . #define LCD_SETGRAPHICMODE 0x1f LiquidCrystal lcd(9, 4, 8, 7, 6, 5); void setGraphicMode(){ lcd.command(LCD_SETGRAPHICMODE); } . . . . .
No necesitamos ninguna tabla rusa aquí, por lo tanto, se utiliza el LiquidCrystal estándar (no enderezado), que funciona perfectamente en modo gráfico. Para no perder el tiempo con la depuración de todas las opciones de la biblioteca, en el caso de que el texto y las pantallas gráficas se incluyan en paralelo, entonces para cada uso mi propia biblioteca (para texto actualizado Rus_OLED, para gráficos normales). En este caso, la conexión todavía se puede hacer a las mismas patas del controlador, con la excepción de los pines de salida E, de acuerdo con el diagrama anterior.
Además, utilicé parcialmente los logros del autor de la biblioteca WinstarOLED mencionada (en sí mismo, este complemento para LuquidCrystal es, en mi opinión, incompleto, y no es práctico usarlo como). Introdujo una función conveniente para configurar el cursor gráfico (aquí se solucionó el error original con respecto al valor
x máximo):
void setGraphicCursor( uint8_t x, uint8_t y ){ if( 0 <= x && x <= 99 ){ lcd.command(LCD_SETDDRAMADDR | x); } if( 0 <= y && y <= 1 ){ lcd.command(LCD_SETCGRAMADDR | y); } }
La constante LCD_SETDDRAMADDR se define en la biblioteca LiquidCrystal. Una pantalla de 100x16, como una pantalla de texto, se divide en dos líneas, 0 y 1, porque
y solo puede tomar dos valores aquí. Y la coordenada horizontal
x varía de 0 a 99. Se envía un byte con el comando lcd.write (), cuyos bits individuales determinan las posiciones luminosas de la línea vertical con una longitud de 8 puntos. La posición más a la izquierda en la línea superior tiene coordenadas 0,0, la más a la derecha en la parte inferior - 99,1. Además, el punto más bajo corresponderá al bit menos significativo, y el punto más bajo, el más alto.
Para la conveniencia de codificar imágenes, dibujé una placa en la que puede crear rápidamente el código deseado manualmente. Para tablas de fuentes completas, por supuesto, es aconsejable usar editores especiales (hay al menos un millón de grados diferentes de actividad de aficionados), pero 10 dígitos con el orden de bits deseado son más rápidos de procesar manualmente, especialmente dado que las fuentes creadas automáticamente a menudo todavía tienen que terminarse a mano. De acuerdo con lo anterior, un glifo, por ejemplo, la fuente número 2 10x16 se codificará de la siguiente manera:

Todo esto está escrito en una matriz bidimensional de la forma:
const byte Data2[2][10]={{0x06,0x07,0x03,0x03,0x03,0x83,0xc3,0x63,0x3f,0x1e}, {0xf0,0xf8,0xcc,0xc6,0xc3,0xc1,0xc0,0xc0,0xc0,0xc0}};
Para cada dígito 0-9, se crea una matriz de este tipo: Data0, Data1, Data2, etc. Para los relojes, además de los números, también necesitará un doble punto. Se puede acortar:
const byte DataDP[2][2]={{0x70,0x70}, {0x1c,0x1c}};//
Como el controlador no sabe cómo "parpadear" en el modo gráfico, es necesario parpadear mediante programación el colon. Puede extinguir un punto doble simplemente mostrando ceros en las posiciones correspondientes, pero por uniformidad hice una matriz separada
const byte DataDPclr[2][2]={{0x00,0x00}, {0x00,0x00}};// .
Para mostrar cada dígito y por separado para un punto doble, se escribe una función separada:
void draw2 (byte x/* */) // “2” { for (byte i = x; i<x+10; i++){ setGraphicCursor(i, 0); lcd.write(Data2[0][ix]); setGraphicCursor(i, 1); lcd.write(Data2[1][ix]);} }
Todas las funciones son iguales, pero usan diferentes matrices y, para un punto doble, también se usan otros límites del bucle. Resultó no ser demasiado económico en términos de la cantidad de código (ver más sobre esto más adelante), pero es claro y fácil de corregir errores. Los espacios entre los caracteres se tienen en cuenta en la etapa de salida, lo que indica la posición correspondiente (la
biblioteca RTClib se usa para leer el reloj):
void loop() { DateTime clock = RTC.now(); if (clock.second()!=old_second) { uint8_t values; values=clock.hour()/10; // drawValPos(values,0); values=clock.hour()%10; // drawValPos(values,12); values=clock.minute()/10; // drawValPos(values,28); values=clock.minute()%10; //end drawValPos(values,40); if (clock.second()%2) drawDP(24); else drawDPclr(24); old_second=clock.second(); }//end if clocksecond }
Diez dígitos de 20 bytes cada uno ocuparán 200 bytes en la memoria, aproximadamente el 10% de su volumen (y la fuente ancha es 16x16, como en el ejemplo a continuación, y todo el 16%). Una fuente monolingüe completa de este tamaño, junto con números, sin tener en cuenta todo tipo de signos de puntuación y especiales. caracteres, contiene de 62 (inglés) a 74 (ruso sin E) caracteres, el valor ocupará casi la mitad de la RAM ATmega328. Por lo tanto, los trucos con matrices y funciones de salida por separado para cada personaje deberán cancelarse y hacer lo que se espera. Es decir, las fuentes deben dejarse en la memoria del programa y descargarse a través de PROGMEM, y todos los patrones de glifos deben organizarse como una sola matriz de fuentes y cargarse para la salida por número de símbolo en una sola tabla. De lo contrario, no habrá suficiente memoria y el código del programa se inflará a un volumen incontrolable. Aquí no nos detendremos en esto, porque en nuestros ejemplos simples no se requiere todo esto; cada vez nos limitaremos a un pequeño número estrictamente necesario de caracteres.
Debido al gran tamaño del texto completo del boceto GraphicOLED_DC1307, no lo traigo; puedes descargarlo
aquí . La función resetOLED se guarda en el texto, que distorsiona la potencia de la pantalla cuando el controlador se reinicia (a través de pwrPin D2), pero nunca se ha necesitado, por lo que se puede quitar de forma segura. El resultado del programa se muestra en la foto:

Desafortunadamente, se excluye la permanencia simultánea en modo texto y gráfico, por lo tanto, si desea utilizar el espacio restante, deberá dibujar sus propias fuentes (queda espacio para aproximadamente 7 caracteres de fuente 5x7 en cada línea).
Pantalla gráfica WEG010016A
Cuando, finalmente, llegaron las pantallas gráficas ordenadas WEG010016AL, comencé tratando de ingresarlas en modo de texto para ver qué sucedía.
Para verificar el modo de texto
, se descargó un programa para simular una pantalla de reloj de calendario con un sensor de temperatura externo, descrito
en una publicación anterior . El resultado me hizo recordar que diferentes pantallas Winstar pueden orientarse de manera diferente con respecto al conector (en este caso, el WEG010016A tiene un conector en la parte superior, para el texto WEH001602B, que usamos arriba, en la parte inferior, y para el tipo C en el lado):

Nos ocuparemos más de la orientación de la pantalla, pero por ahora veremos qué sucedió. Pero no resultó nada bueno: el modo de texto (por supuesto, equipado con una muleta, que se discutió al principio del artículo) funciona a la perfección, pero en la práctica no tiene sentido debido a la falta de espacios entre los caracteres. Por lo tanto, no nos detendremos en él, sino que procederemos a considerar el modo gráfico.
Los procedimientos de instalación en modo gráfico en sí son los mismos que los discutidos anteriormente para la versión de texto. Queda por tratar con la tapa de la pantalla si tiene un conector en la parte superior de la pantalla. Por supuesto, puede voltear la pantalla, pero la posición con el conector hacia abajo me parece más natural y conveniente. Además, cuando use un tipo con un conector en el lateral, es posible que deba orientar el conector hacia la derecha en lugar de hacia la izquierda. Para la orientación "al revés", es necesario transformar la imagen, es decir, intercambiar las primeras y últimas posiciones horizontales, líneas, y también invertir el orden de los bits en los bytes que componen la matriz (el bit menos significativo corresponderá al punto inferior).
Como ya había pintado diez dígitos para el caso anterior, para la última tarea quedaba por introducir el procedimiento de reversión del programa:
byte reverse(byte x) { byte result=0,i; for(i=0;i<8;i++) { if (x & (1 << i)) { result |= 1 << (7-i); } } return result; }
Puede cambiar el orden de las coordenadas horizontales y las líneas verticales haciendo cambios en la función setGraphicCursor:
void setGraphicCursor( uint8_t x, uint8_t y ){ if( 0 <= x && x <= 99 ){ lcd.command(LCD_SETDDRAMADDR | (99-x)); } if( 0 <= y && y <= 1 ){ lcd.command(LCD_SETCGRAMADDR | (1-y)); } }
Las funciones de salida de la matriz de cada dígito permanecen iguales, solo se agrega la inversión de bits:
void draw2 (byte x/* */) // 2 { for (byte i = x; i<x+10; i++){ setGraphicCursor(i, 0); byte b=reverse(Data2[0][ix]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(Data2[1][ix]); lcd.write(b);} }
El boceto completo de la salida del reloj GraphicOLED_DC1307_100x16 se puede descargar
desde aquí , y el resultado para la pantalla WEG010016AL se muestra en la foto:

Pero en esta foto, una fuente de un tipo diferente (16x16) en la pantalla WEG010016CG (la pantalla también está al revés):

Si vuelve a crear la fuente cambiando el orden de los bits manualmente, entonces no hay necesidad de hacer lo contrario y el programa se ejecutará más rápido (aunque no hay retrasos notables en el ojo). Pero el procedimiento de cambio de bits dado es útil en cualquier caso: para mostrar varias imágenes. Por ejemplo, desde una flecha que apunta hacia arriba y hacia la derecha, puede obtener cuatro direcciones a la vez mediante programación.
Dibujo de flechaImagen y código de flecha (las coordenadas y los bits en la tabla se invierten de acuerdo con la posición inferior del conector para la pantalla WEG010016AL, ver arriba):

const byte DataATR[2][8]={{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x01,0x02,0x04,0x28,0x30,0x78,0x60,0x80}};
Oportunidades para flechas multidireccionales
. . . . . void drawSW (byte x) // ( ) { for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); lcd.write(DataATR[0][ix]); setGraphicCursor(i, 1); lcd.write(DataATR[1][ix]);} } void drawNW (byte x) // ( ) {// : for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); byte b=reverse(DataATR[1][ix]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(DataATR[0][ix]); lcd.write(b);} } void drawNE (byte x) // ( ) {// , for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); byte b=reverse(DataATR[1][7-(ix)]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(DataATR[0][7-(ix)]); lcd.write(b);} } void drawSE (byte x) // ( ) {// for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); lcd.write(DataATR[0][7-(ix)]); setGraphicCursor(i, 1); lcd.write(DataATR[1][7-(ix)]);} } . . . . .
La foto a continuación muestra el resultado de un programa en blanco para mostrar el sensor de velocidad y dirección del viento. Como puede ver, resultó muy simple implementar fuentes de diferentes tamaños en una línea junto con imágenes:

En conclusión, agregaré que
aquí hay una biblioteca muy interesante para trabajar con WS0010 en modos gráficos y de texto usando SPI. En el texto, copia principalmente Crystal Crystal (¿y en qué más se te ocurre?), Y en el gráfico tiene las funciones de dibujar primitivas gráficas, fuentes incorporadas (gruesas, como las mías, y las habituales 5x7) y mucho más.